尝试添加子记录时尝试插入现有父记录的实体框架

时间:2018-10-04 17:34:28

标签: asp.net-mvc entity-framework api

我刚开始使用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复制错误。

如果尚不存在,如何将其仅发送到所有终端?似乎遵循的模式与其他类似方法所采用的模式相同,因此我无法弄清楚为什么这样做与众不同。

1 个答案:

答案 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而失败。