13

コード内の多対多の関係を保存するコードがいくつかあります。Entity Framework 4.1では正常に機能していましたが、Entity Framework 5に更新した後、失敗します。

次のエラーが発生します。

INSERTステートメントがFOREIGNKEY制約「FK_WebUserFavouriteEvent_Event」と競合しました。データベース「MainEvents」、テーブル「dbo.Event」、列「Id」で競合が発生しました。

カスタムマッピングでPOCOエンティティを使用しています。標準フィールドと多対1の関係マッピングは正常に機能しているようです。

アップデート

さて、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 FluentAPI-RelationshipsMSDNの記事を参照してください。

注:これも保存に制限されておらず、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

これを見ると、同じ関係を2回マッピングしたことが問題の原因である可能性があります。そして、あなたはそれを異なる順序でマッピングします。

私は最初に関係を一度マッピングした簡単なテストを行いました:

    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

生成する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にデータベースを作成させます。つまり、おそらく列名を切り替えるだけで、外部キーを見るとそれらも切り替えられます。データベースを手動で作成した場合は、おそらくそうではありません。

つまり、あなたのマッピングは等しくなく、そのうちの1つが使用されることを期待しており、その1つはおそらく間違っています。以前のバージョンでは、EFがそれらを異なる順序でピックアップしたと思います。

更新: 外部キーについて少し興味があり、SQLを確認しました。

最初のコードから:

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

そして2番目のコードから:

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

今ではそれは良くありません。Childrenに対してマップされたParentIdは、確かに私たちが望んでいるものではありません。

では、2番目のマッピングは間違っていますか?私が最初のものを削除したときに何が起こったのかを見てください。

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

どういうわけか、2つのマッピングがあると、物事が台無しになります。バグかどうかはわかりません。

于 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 に答える