13

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

我收到以下错误:

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

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

更新

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

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 - 关系 MSDN 文章

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

以下是所有相关代码:

服务层

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

2 回答 2

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

    }
}

然后我将 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

不知何故,有两个映射似乎把事情搞砸了。有没有BUG我不知道。

于 2012-09-02T17:16:22.783 回答
6

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

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

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

要解决方法,我认为您需要:

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

带来不便敬请谅解。

更新:这是错误

于 2012-09-06T20:37:15.847 回答