代码之家  ›  专栏  ›  技术社区  ›  Charlino

实体框架5升级后,多对多关系左右键翻转

  •  13
  • Charlino  · 技术社区  · 13 年前

    我有一些代码可以在代码中保存多对多关系。它在实体框架4.1中运行良好,但在更新到实体框架5后,它失败了。

    我收到以下错误:

    INSERT语句与FOREIGN KEY约束“FK_WebUserFavouriteEvent_Event”冲突。冲突发生在数据库“MainEvents”、表“dbo.Event”、列“Id”中。

    我正在使用带有自定义映射的POCO实体。标准字段和多对一关系映射似乎运行良好。

    更新

    好的,所以我已经安装了SQL事件探查器,并且绘图已经变厚了。。。

    exec sp_executesql N'insert [dbo].[WebUserFavouriteEvent]([WebUserId], [EventId])
    values (@0, @1)
    ',N'@0 int,@1 int',@0=1820,@1=14
    

    这意味着:

    WebUserId = @0 = 1820
    EventId = @1 = 14
    

    有趣的是 EF5似乎把外键翻过来了 这个 WebUserId应为14 以及 EventId应为1820 ,而不是像现在这样。

    我查看了映射代码,我99%都设置正确了。看见 Entity Framework Fluent API - Relationships MSDN article 了解更多信息。

    注意: 我还发现,这也不仅限于保存,SELECTs也被破坏了。

    以下是所有相关代码:

    服务层

    public void AddFavEvent(WebUser webUser, Event @event)
    {
        webUser.FavouriteEvents.Add(@event);
    
        _webUserRepo.Update(webUser);
    }
    

    存储库

    public void Update<T>(params T[] entities)
        where T : DbTable
    {
        foreach (var entity in entities)
        {
            entity.UpdatedOn = DateTime.UtcNow;
        }
    
        _dbContext.SaveChanges();
    }
    

    注意: 我使用的是每个请求1个DataContext的方法,所以 webUser @event 将从与 _webUserRepo

    实体 (不要担心DbTable之类的东西)

    public class Event : DbTable
    {
        //BLAH
        public virtual ICollection<WebUser> FavouriteOf { get; set; }
        //BLAH
    }
    
    public class WebUser : DbTable
    {
        //BLAH
        public virtual ICollection<Event> FavouriteEvents { get; set; }
        //BLAH
    }
    

    映射

    public class EventMapping : DbTableMapping<Event>
    {
        public EventMapping()
        {
            ToTable("Event");
            //BLAH
            HasMany(x => x.FavouriteOf)
                .WithMany(x => x.FavouriteEvents)
                .Map(x =>
                         {
                             x.MapLeftKey("EventId");
                             x.MapRightKey("WebUserId");
                             x.ToTable("WebUserFavouriteEvent");
                         });
        }
    }
    
    public class WebUserMapping : DbTableMapping<WebUser>
    {
        public WebUserMapping ()
        {
            HasMany(x => x.FavouriteEvents)
                .WithMany(x => x.FavouriteOf)
                .Map(m =>
                         {
                             m.MapLeftKey("WebUserId");
                             m.MapRightKey("EventId");
                             m.ToTable("WebUserFavouriteEvent");
                         });
        }
    }
    
    2 回复  |  直到 13 年前
        1
  •  13
  •   roufamatic RichardJohnn    10 年前

    看到这一点,我怀疑问题可能是由您两次映射同一关系这一事实引起的。你可以按不同的顺序绘制它。

    我做了一个简单的测试,第一次映射了关系:

        class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
            var p = new Parent();
            var c = new Child();
            using (var db = new Context())
            {
                db.Parents.Add(new Parent());
                db.Parents.Add(p);
    
                db.Children.Add(c);
                db.SaveChanges();
            }
    
            using (var db = new Context())
            {
                var reloadedP = db.Parents.Find(p.ParentId);
                var reloadedC = db.Children.Find(c.ChildId);
    
                reloadedP.Children = new List<Child>();
                reloadedP.Children.Add(reloadedC);
    
                db.SaveChanges();
            }
    
            using (var db = new Context())
            {
                Console.WriteLine(db.Children.Count());
                Console.WriteLine(db.Children.Where(ch => ch.ChildId == c.ChildId).Select(ch => ch.Parents.Count).First());
                Console.WriteLine(db.Parents.Where(pa => pa.ParentId == p.ParentId).Select(pa => pa.Children.Count).First());
            }
        }
    }
    
    public class Parent
    {
        public int ParentId { get; set; }
        public ICollection<Child> Children { get; set; }
    
    }
    
    public class Child
    {
        public int ChildId { get; set; }
        public ICollection<Parent> Parents { get; set; }
    }
    
    public class Context : DbContext
    {
        public Context() : base("data source=Mikael-PC;Integrated Security=SSPI;Initial Catalog=EFTest")
        {
    
        }
    
        public IDbSet<Child> Children { get; set; }
        public IDbSet<Parent> Parents { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Child>()
                .HasMany(x => x.Parents)
                .WithMany(x => x.Children)
                .Map(c =>
                {
                    c.MapLeftKey("ChildId");
                    c.MapRightKey("ParentId");
                    c.ToTable("ChildToParentMapping"); 
                });
    
        }
    }
    

    然后我将OnModelCreating更改为:

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Child>()
                .HasMany(x => x.Parents)
                .WithMany(x => x.Children)
                .Map(c =>
                {
                    c.MapLeftKey("ChildId");
                    c.MapRightKey("ParentId");
                    c.ToTable("ChildToParentMapping"); 
                });
    
            modelBuilder.Entity<Parent>()
               .HasMany(x => x.Children)
               .WithMany(x => x.Parents)
               .Map(c =>
               {
                   c.MapLeftKey("ParentId");
                   c.MapRightKey("ChildId");
                   c.ToTable("ChildToParentMapping");
               });
        }
    

    我发现并怀疑的是,第一次运行会生成以下sql:

    exec sp_executesql N'insert [dbo].[ChildToParentMapping]([ChildId], [ParentId])
    values (@0, @1)
    ',N'@0 int,@1 int',@0=1,@1=2
    

    与生成以下内容的第二个相反:

    exec sp_executesql N'insert [dbo].[ChildToParentMapping]([ParentId], [ChildId])
    values (@0, @1)
    ',N'@0 int,@1 int',@0=1,@1=2
    

    你看到价值观翻转了吗?在这里,它实际上将ChildId列计算为ParentId。现在这对我来说并没有崩溃,但我让EF创建了数据库,这意味着它可能只是切换列名,如果我查看外键,它们也会被切换。如果您手动创建数据库,情况可能不会如此。

    简而言之:你们的映射是不相等的,我希望使用其中一个映射,而那个映射可能是错误的。在早期的版本中,我猜EF是以不同的顺序获得它们的。

    更新: 我对外键有点好奇,于是检查了sql。

    从第一个代码开始:

    ALTER TABLE [dbo].[ChildToParentMapping] ADD CONSTRAINT [FK_dbo.ChildToParentMapping_dbo.Children_ChildId] FOREIGN KEY ([ChildId]) REFERENCES [dbo].[Children] ([ChildId]) ON DELETE CASCADE
    

    从第二个代码来看:

    ALTER TABLE [dbo].[ChildToParentMapping] ADD CONSTRAINT [FK_dbo.ChildToParentMapping_dbo.Children_ParentId] FOREIGN KEY ([ParentId]) REFERENCES [dbo].[Children] ([ChildId]) ON DELETE CASCADE
    

    现在这不太好。ParentId映射到Children肯定不是我们想要的。

    那么第二个映射是错误的吗?不是真的,因为看看我去掉第一个时发生了什么:

    ALTER TABLE [dbo].[ChildToParentMapping] ADD CONSTRAINT [FK_dbo.ChildToParentMapping_dbo.Parents_ParentId] FOREIGN KEY ([ParentId]) REFERENCES [dbo].[Parents] ([ParentId]) ON DELETE CASCADE
    

    不知怎么的,有两个映射似乎会把事情搞砸。我不知道是不是虫子。

        2
  •  6
  •   Andrew Peters    13 年前

    我可以确认这是EF5中的一个错误,尽管我仍然不确定它在4.3.1中是如何工作的。

    问题是我们没有正确地将LeftKey/RightKey调用与其相应的导航属性相关联。

    我将在我们的CodePlex项目网站上提交一个EF6错误。

    要解决此问题,我认为您需要:

    1. 仅从一侧配置关联。或
    2. 在两种配置中,将LeftKey/RightKey列的名称切换为相同。

    很抱歉给您带来不便。

    更新 :这是 bug