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

Cha*_*ino 13 c# sql-server entity-framework entity-framework-5

我有一些代码可以在代码中保存多对多的关系.它与Entity Framework 4.1一起运行良好,但在更新到Entity Framework 5后,它失败了.

我收到以下错误:

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

我正在使用POCO实体和自定义映射.标准字段和多对一关系映射似乎工作正常.

UPDATE

好的,所以我安装了SQL Profiler并且情节已经变厚......

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

意思是:

WebUserId = @0 = 1820
EventId = @1 = 14
Run Code Online (Sandbox Code Playgroud)

有趣的是,EF5似乎已经翻转了外键 ...... WebUserId应该是14,EventId 应该是1820,而不是像现在这样.

我查看了映射代码,我99%我已经正确设置了它.有关详细信息,请参阅实体框架Fluent API - 关系MSDN文章.

注意:我还发现这不仅限于保存,SELECT也被破坏.

这是所有相关代码:

服务层

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

    _webUserRepo.Update(webUser);
}
Run Code Online (Sandbox Code Playgroud)

知识库

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

    _dbContext.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

注意:我正在使用每个请求方法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
}
Run Code Online (Sandbox Code Playgroud)

映射

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");
                     });
    }
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*son 13

看着这个,我怀疑这个问题可能是因为你将同一个关系映射两次.然后以不同的顺序映射它.

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

    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"); 
            });

    }
}
Run Code Online (Sandbox Code Playgroud)

然后我将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");
           });
    }
Run Code Online (Sandbox Code Playgroud)

我发现和怀疑的是第一次运行生成这个sql:

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

与产生的第二个相反:

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

你看到价值被翻转了吗?这里它实际上将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
Run Code Online (Sandbox Code Playgroud)

从第二个代码:

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

现在这不好.针对儿童映射的ParentId肯定不是我们想要的.

那么第二个映射错了吗?不是真的因为看到我删除第一个时发生的事情:

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

不知何故有两个映射似乎搞砸了.不管是不是我不知道.


And*_*ers 6

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

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

我将在CodePlex项目网站上提交EF6错误.

为了解决方法,我认为你需要:

  1. 仅从一侧配置关联.要么,
  2. 在左侧配置中切换LeftKey/RightKey列名称是相同的.

抱歉给你带来不便.

更新:这是错误.