1

私は、EF で一般的な孤児の問題に対する満足のいく解決策を見つけるために、かなりの時間を探し回りました。

孤立化の最も単純な形式の 1 つは、エンティティのコレクションの消去です。エンティティ間の関係は削除されますが、子エンティティはデータベースに残ります。

私の要件: -

  • コレクションのクリアはドメインで発生するため、単純に clear を呼び出して、それ以上呼び出せないようにしたいと考えています。
  • 親と子の間の関係が壊れて削除が発生したかどうかを判断するロジックは、リポジトリ/DbContext 内にカプセル化する必要があります。
  • この問題を解決するために、何かを追加してドメインを「汚す」必要はありません。これには後方参照が含まれます。

解決策を探すのにかなりの時間を費やしたので、これは解決できないと思いますが、希望を持って質問します!

私が調べた領域は、ChangeTracker と、さまざまな場所でポップアップした AssociationChanged イベントと同様に、フックできる可能性のあるイベントです。DbContext のどこかで、この関係が壊れていることを認識している必要があります。それにアクセスする方法、それが問題ですか?

ありがとう。

4

1 に答える 1

0

以下の解決策をお試しいただけますか? それはあなたのニーズに合っています。DeleteOrphans 拡張メソッドは、DetectChanges メソッドと SaveChanges メソッドの間で呼び出す必要があります。

public static class DbContextExtensions { private static readonly ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>> s_navPropMappings = new ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>>();

    public static void DeleteOrphans( this DbContext source )
    {
        var context = ((IObjectContextAdapter)source).ObjectContext;
        foreach (var entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
        {
            var entityType = entry.EntitySet.ElementType as EntityType;
            if (entityType == null)
                continue;

            var navPropMap = s_navPropMappings.GetOrAdd(entityType, CreateNavigationPropertyMap);
            var props = entry.GetModifiedProperties().ToArray();
            foreach (var prop in props)
            {
                NavigationProperty navProp;
                if (!navPropMap.TryGetValue(prop, out navProp))
                    continue;

                var related = entry.RelationshipManager.GetRelatedEnd(navProp.RelationshipType.FullName, navProp.ToEndMember.Name);
                var enumerator = related.GetEnumerator();
                if (enumerator.MoveNext() && enumerator.Current != null)
                    continue;

                entry.Delete();
                break;
            }
        }
    }

    private static ReadOnlyDictionary<string, NavigationProperty> CreateNavigationPropertyMap( EntityType type )
    {
        var result = type.NavigationProperties
            .Where(v => v.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            .Where(v => v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || (v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne && v.FromEndMember.GetEntityType() == v.ToEndMember.GetEntityType()))
            .Select(v => new { NavigationProperty = v, DependentProperties = v.GetDependentProperties().Take(2).ToArray() })
            .Where(v => v.DependentProperties.Length == 1)
            .ToDictionary(v => v.DependentProperties[0].Name, v => v.NavigationProperty);

        return new ReadOnlyDictionary<string, NavigationProperty>(result);
    }
}

于 2015-09-09T17:37:40.553 に答える