1

最初のEF4.3コード(ただし、EFによって生成されたデータベースとは対照的に、明示的に設計されたデータベースを使用)では、次の問題が発生します。

1対多の「Break」エンティティを含むことができるエンティティ「WorkPlan」があります。モデルでは、作業計画にはICollectionがありますが、Breakは作業計画について認識していません。

これは集約的な関係です。「ブレーク」は、作業計画の範囲外に存在することはできません。

私がしたいのは、WorkPlanのブレークのコレクションからブレークを削除するときに、変更を保存するときにそのブレークをデータベースから削除する必要があるということです。

[Test]
public void ShouldRemoveBreakInDatabase()
{
    // Setup
    var workPlan = WorkPlanBuilder.Build(x => x.AddBreak());
    Save(workPlan);

    // Exercise
    var exerciseContext = CreateDataContext();
    workPlan = exerciseContext.WorkPlans.Single();
    workPlan.RemoveBreak(workPlan.Breaks.Single());
    exerciseContext.SaveChanges();

    // Verify            
    var actual = SqlHelper.ExecuteScalar("select count(*) from Breaks");
    Assert.That(actual, Is.EqualTo(0));
}

ただし、SaveChanges()呼び出しでは、次の例外が発生します。

System.Data.Entity.Infrastructure.DbUpdateException:関係の外部キープロパティを公開していないエンティティの保存中にエラーが発生しました。単一のエンティティを例外のソースとして識別できないため、EntityEntriesプロパティはnullを返します。保存中の例外の処理は、エンティティタイプの外部キープロパティを公開することで簡単にできます。詳細については、InnerExceptionを参照してください。
----> System.Data.UpdateException:エントリの更新中にエラーが発生しました。詳細については、内部例外を参照してください。
----> System.Data.SqlClient.SqlException:値NULLを列'WorkPlan_Id'、テーブル'ActivityStore.dbo.Breaks'に挿入できません。列はnullを許可しません。UPDATEは失敗します。ステートメントは終了されました。

WorkPlanのコレクションからBreakを削除するとき、EFはデータベースでWorkPlan_Idフィールドをnullに設定する必要があると想定しますが、フィールドはnull可能ではないことは明らかです。

データコンテキストに以下を追加します。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<WorkPlan>().HasMany(x => x.Breaks).WithRequired();
}

別の例外が発生します:

System.Data.Entity.Infrastructure.DbUpdateException:関係の外部キープロパティを公開していないエンティティの保存中にエラーが発生しました。単一のエンティティを例外のソースとして識別できないため、EntityEntriesプロパティはnullを返します。保存中の例外の処理は、エンティティタイプの外部キープロパティを公開することで簡単にできます。詳細については、InnerExceptionを参照してください。
----> System.Data.UpdateException:「WorkPlan_Breaks」AssociationSetからの関係は「削除済み」状態です。多重度の制約がある場合、対応する「WorkPlan_Breaks_Target」も「Deleted」状態である必要があります。

これを機能させる簡単な方法はありますか?

4

2 に答える 2

0

私は解決策を思いつきました。しかし、私はこれよりも純粋な構成を好むでしょう。

しかし、少なくとも私の DataContext 実装には分離されています。

    private DbSet<WorkPlan> _workPlans;
    public DbSet<WorkPlan> WorkPlans
    {
        get { return _workPlans; }
        set 
        { 
            _workPlans = value;
            _workPlans.Local.CollectionChanged += LocalWorkPlansChanged;
        }
    }

    private void LocalWorkPlansChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action != NotifyCollectionChangedAction.Add)
            return;

        foreach (var workPlan in e.NewItems.Cast<WorkPlan>())
        {
            var collection = workPlan.Breaks as EntityCollection<Break>;
            if (collection == null)
                continue;
            collection.AssociationChanged += WorkPlanBreaksAssociationChanged;
        }
    }

    private void WorkPlanBreaksAssociationChanged(object sender, CollectionChangeEventArgs e)
    {
        if (e.Action == CollectionChangeAction.Remove)
        {
            var @break = (Break)e.Element;
            Breaks.Remove(@break);
        }
    }

そう、

ステップ 1: DbSet のメモリー内の WorkPlan コレクションが変更されるたびに発生するイベントに接続します。

ステップ 2: WorkPlan の変更が新しいオブジェクトによるものかどうかを判断します。これは、新しくインスタンス化されたオブジェクト、またはデータ ストアから読み込まれたオブジェクトを意味する場合があります。

手順 3: 追加されたオブジェクトの WorkPlan.Breaks コレクションを確認します。コレクションが EntityCollection<> の場合、オブジェクトはデータベースからロードされています。AssociationChanged イベントをフックして、関連付けが変更されたときに通知を受け取ります。

ステップ 4: アソシエーション変更イベントを受信したら、それが "Remove" イベントであるかどうかを確認し、そうである場合は Breaks DbSet から明示的に Break を削除します。

より単純なソリューションは大歓迎です。

于 2012-07-18T09:10:38.223 に答える
0

私は同じ問題を抱えていました.1.5時間のグーグル検索の後、解決策を見つけました. このようなタスクを達成するための鍵は、いわゆる「識別関係」です。これらは、エンティティが他のエンティティの子としてのみ「生きている」ことをEFに伝える方法であるため、親からエンティティを削除すると、データベースからも削除する必要があります。関連する質問を参照してください:

要約すると、私のソリューションは次のようになりました。

public class Item
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    [Key, ForeignKey("Group"), Column(Order = 1)]
    public int GroupId { get; set; }
    public ItemGroup Group { get; set; }
}

public class ItemGroup
{
    public int Id { get; set; }
    public ICollection<Item> Items { get; set; }
}

データベースの Item.Id は ID 列で、GroupId は外部キーです (EF で自動的に db を生成しません)。

于 2013-03-26T15:18:20.307 に答える