我刚开始使用EF(以前曾使用过Dapper)较新,并且在我接管的大多数API应用程序中遇到了问题。
我有一个创建或更新userTerminal
的方法(已建立以前的开发人员)。我要添加到Terminal
的当前记录与userTerminal
有一个FK绑定到已经存在的终端。
这是方法
public void AddTerminalUser(UserTerminal userTerminal)
{
using (var context = new GateManagementEntities(connectionString))
{
//Modified existing
if (context.UserTerminals.Count(z => z.TerminalCode == userTerminal.TerminalCode && z.UserId == userTerminal.UserId) > 0)
{
UserTerminal ut = new UserTerminal() { Id = userTerminal.Id, IsDeleted = true, LastUpdated = DateTime.Now };
var entry = context.Entry(ut);
entry.Property(z => z.LastUpdated).IsModified = true;
entry.Property(z => z.IsDeleted).IsModified = true;
}
else
{
//New Entity
userTerminal.CreatedDate = DateTime.Now;
userTerminal.LastUpdated = DateTime.Now;
context.UserTerminals.Add(userTerminal);
}
context.SaveChanges();
}
}
这是传入的类
public partial class UserTerminal
{
public int Id { get; set; }
public string UserId { get; set; }
public Nullable<int> TerminalCode { get; set; }
public Nullable<bool> IsDeleted { get; set; }
public Nullable<System.DateTime> LastUpdated { get; set; }
public Nullable<System.DateTime> CreatedDate { get; set; }
public string UpdatedBy { get; set; }
public virtual Terminal Terminal { get; set; }
public virtual UserPreference UserPreference { get; set; }
}
public partial class Terminal
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Terminal()
{
this.GateAssignments = new HashSet<GateAssignment>();
this.GateAssignments1 = new HashSet<GateAssignment>();
this.GateDefinitions = new HashSet<GateDefinition>();
this.UserTerminals = new HashSet<UserTerminal>();
}
public int TerminalCode { get; set; }
public string City { get; set; }
public string State { get; set; }
public Nullable<System.DateTime> LastUpdated { get; set; }
public Nullable<System.DateTime> CreatedDate { get; set; }
public string UpdatedBy { get; set; }
public Nullable<int> GateTypeId { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<GateAssignment> GateAssignments { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<GateAssignment> GateAssignments1 { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<GateDefinition> GateDefinitions { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserTerminal> UserTerminals { get; set; }
public virtual GateType GateType { get; set; }
}
TerminalCode
是将Terminal
链接到TerminalUser
的链接
我检查了传入的UserTerminal
对象和UserTerminal的TerminalCode
,并在其中填充了我期望的终端代码(我知道终端代码在终端表中),而Terminal
对象是并完全填充了我期望的TerminalCode
。它试图将已经存在的终端记录插入数据库,从而导致PK复制错误。
如果尚不存在,如何将其仅发送到所有终端?似乎遵循的模式与其他类似方法所采用的模式相同,因此我无法弄清楚为什么这样做与众不同。
答案 0 :(得分:1)
在上下文实例之间传递实体很麻烦。当您的上下文不知道UserTerminal而新的UserTerminal引用了真实的现有Terminal记录时,问题是新的Context实例也不知道该Terminal。
如果在添加UserTerminal之前将Terminal(和任何其他相关实体)加载到上下文中,它应该可以工作,但是为了安全起见,通常最好将新的UserTerminal与上下文所知道的对该终端的引用相关联。
例如:
public void AddTerminalUser(UserTerminal userTerminal)
{
using (var context = new GateManagementEntities(connectionString))
{
//Modified existing
if (context.UserTerminals.Count(z => z.TerminalCode == userTerminal.TerminalCode && z.UserId == userTerminal.UserId) > 0)
{
UserTerminal ut = new UserTerminal() { Id = userTerminal.Id, IsDeleted = true, LastUpdated = DateTime.Now };
var entry = context.Entry(ut);
entry.Property(z => z.LastUpdated).IsModified = true;
entry.Property(z => z.IsDeleted).IsModified = true;
}
else
{
//New Entity
userTerminal.CreatedDate = DateTime.Now;
userTerminal.LastUpdated = DateTime.Now;
var terminal = context.Terminals.Find(userTerminal.TerminalCode);
userTerminal.Terminal = terminal;
// Repeat above for all other references.
context.UserTerminals.Add(userTerminal);
}
context.SaveChanges();
}
}
我不建议传递实体到加载它们的DbContext范围之外。 ViewModels / DTO应该用于此目的,并且在您期望FK引用自动解析时避免出现意外。在某些情况下,上下文有时似乎会解析引用,因为相关实体已被加载,但是在该实体先前未加载到上下文中的情况下,由于重复的PK而失败。