实体框架代码第一 - 孤立解决方案?

时间:2012-11-20 14:04:12

标签: entity-framework-4.1 ef-code-first

我已经搜索了大量时间,试图找到一个令人满意的解决方案来解决EF中常见的孤儿问题。

最简单的孤立形式之一是清算一组实体。删除了实体之间的关系,但子实体仍保留在数据库中。

我的要求: -

  • 清除收集发生在域中,我希望能够简单地调用清除而不再清除。
  • 任何用于确定父级和子级之间的关系是否已被破坏导致删除的逻辑都需要封装在repository / DbContext中。
  • 为了解决这个问题,我不想用其他任何东西“弄脏”域名。这包括背面参考。

我怀疑这无法解决,因为我花了相当多的时间寻找解决方案,但我却没有希望!

我看过的区域是ChangeTracker和我可以挂钩的任何可能的事件,类似于在各个地方弹出的AssociationChanged事件。 DbContext中的某个地方必须知道这种关系已被打破。如何访问它,这是个问题?

感谢。

1 个答案:

答案 0 :(得分:0)

您可以尝试以下解决方案吗?它适合您的需求。必须在DetectChanges和SaveChanges方法之间调用DeleteOrphans扩展方法。

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

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