【问题标题】:Entity Framework - Include / Reference / Collection实体框架 - 包含/引用/集合
【发布时间】:2013-08-06 05:14:40
【问题描述】:

我想知道为什么有用于填充导航属性的单独方法。

如果我在整个集合上工作,我可以在属性或集合上调用 Include

但是,如果我处理单个实体,则根据项目是集合 (Collection) 还是单个引用 (Reference),可以调用两种单独的方法。

有什么办法可以解决这个问题 - 这使事情变得比我认为的必要复杂。谁能解释一下为什么在设计 EF 时决定这样做?

编辑

进一步研究,问题就更深了。我试图做的是创建一种在单个实体上加载集合/导航属性的通用方法。这可以使用 Include 在整个集合上轻松完成。但ReferenceCollection 的方法签名略有不同。

没关系,将不得不将这些调用分散在我的应用程序周围。

例如

dbSet<T>().Include(e => e.Property).Include(e => e.Collection).Include(e => e.Collection.Property) 

似乎一切正常。

但是对单个实体的调用是不同的:

context.Entry(entity).Reference(e => e.Property).Load();
context.Entry(entity).Reference(e => e.Property.Select(e => e.SubProperty)).Load();
context.Entry(entity).Collection(e => e.Collection).Load();

【问题讨论】:

  • 想解释一下我怎样才能让这个问题变得更好?
  • 我没有投票结束,但我确实发现第一次很难理解这个问题。也许值得为 Collection/Reference 添加一些示例代码,然后在引用您想要实现的调用示例时?
  • 我假设“在单个实体上工作”是指使用context.Entry(entity).Collection("collection").Load() 方法?
  • @Simon - 您可以通过向我们展示您的尝试、遇到的错误并提出具体问题来改进这一点。
  • 投票结束是因为 S/O 更多地针对具有特定答案的问题,例如“为什么此代码不起作用?/因为您忘记使用 X 参数”。 S/O 特别不鼓励会引发扩展讨论的问题(例如,“他们在设计功能 X 时在想什么”)——不是因为它们不是一个有趣和有用的问题——而是因为 S/O 不是为此而设计的。尝试在与计算机科学相关的 Stack Exchange 网站之一上提问 :)

标签: c# entity-framework navigation-properties


【解决方案1】:

Include() 方法的唯一目的是在查询时显式地预先加载相关数据。

另一方面,Entry() 方法旨在让您对附加到上下文的实体的当前状态进行特定控制,而不仅仅是Load() 相关数据。

这就是为什么您必须在 CollectionReferenceProperty 方法之间进行明确选择的原因,每个方法都公开了不同的功能集(因此返回不同的类型)。

例如:

  • 标量 (DbPropertyEntry) 包含IsModified 属性,表示值是否从“x”更改为“y”(例如)。

  • Reference (DbReferenceEntry) 包含IsLoaded 属性,该属性表示是否已经从数据库中加载了引用的数据。

  • 参考集合 (DbCollectionEntry) 派生自ICollection(因此也是IEnumerable),这意味着您可以迭代其数据。但是,它不能包含 IsModified 属性,因为它可能因集合中的每个项目而异。

不过,如果您只对Load() 感兴趣,您可以利用多态Member() 方法(返回DbMemberEntry,这是上述所有类型的基本类型)并检查条目是否为“可加载”:

var memberEntry = this.Entry(entity).Member("NavigationProperty");

if (memberEntry is DbCollectionEntry collectionMember)
    collectionMember.Load();

if (memberEntry is DbReferenceEntry referenceMember)
    referenceMember.Load();

【讨论】:

  • 感谢@haim770 - 会员功能可能会有所帮助!
  • 谢谢。这真的很有帮助。
  • 虽然部分有用,但原始问题使用表达式来引用属性,而解决方案使用字符串。不幸的是,没有一种简单的方法可以将一个翻译成另一个。如果“成员”也包含属性表达式,这将是最终的解决方案,但我仍在寻找它。
【解决方案2】:

你可以这样做:

1.- 加载包含集合的实体:

MyClass myObject = dbContext.MyClasses
                    .Include(cls => cls.ObjectCollection)
                    .Single(cls => cls.Pk == entityPk);

2.- 然后您必须检索该对象条目并告诉 EF 在集合对象中加载所需的属性:

dbContext.Entry(myObject).Collection("ObjectCollection").Query().Include("ReferenceClass").Load(); 

进一步阅读:

http://msdn.microsoft.com/en-us/data/jj574232#explicitFilter

【讨论】:

    【解决方案3】:

    如果您想继续支持属性表达式以便可以支持“包含”语法 FindAsync,以下解决方案会检索引用属性的 PropertyInfo,然后使用 Expression.Convert,以便您现在可以支持您的 context.Entry(entity).Member(e =&gt; e.Property).Load() 语法希望。

    将以下两个类添加到某个命名空间并在您的类中添加using

    public class MemberEntry
    {
        /// <summary>
        /// If this MemberEntry refers to a CollectionEntry, this will be not null
        /// </summary>
        public CollectionEntry? CollectionEntry { get; init; }
        /// <summary>
        /// If this MemberEntry refers to a ReferenceEntry, this will be not null
        /// </summary>
        public ReferenceEntry? ReferenceEntry { get; init; }
        public MemberEntry(CollectionEntry collectionEntry)
        {
            this.CollectionEntry = collectionEntry;
        }
        public MemberEntry(ReferenceEntry referenceEntry)
        {
            this.ReferenceEntry = referenceEntry;
        }
        //
        // Summary:
        //     Loads the entity or entities referenced by this navigation property, unless Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.IsLoaded
        //     is already set to true.
        //     Note that entities that are already being tracked are not overwritten with new
        //     data from the database.
        public void Load()
        {
            if (this.CollectionEntry != null)
            {
                this.CollectionEntry.Load();
            }
            else
            {
                this.ReferenceEntry!.Load();
            }
        }
    
        //
        // Summary:
        //     Loads the entity or entities referenced by this navigation property, unless Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.IsLoaded
        //     is already set to true.
        //     Note that entities that are already being tracked are not overwritten with new
        //     data from the database.
        //     Multiple active operations on the same context instance are not supported. Use
        //     'await' to ensure that any asynchronous operations have completed before calling
        //     another method on this context.
        //
        // Parameters:
        //   cancellationToken:
        //     A System.Threading.CancellationToken to observe while waiting for the task to
        //     complete.
        //
        // Returns:
        //     A task that represents the asynchronous operation.
        public Task LoadAsync(CancellationToken cancellationToken = default)
        {
            if (this.CollectionEntry != null)
            {
                return this.CollectionEntry.LoadAsync(cancellationToken);
            }
            else
            {
                return this.ReferenceEntry!.LoadAsync(cancellationToken);
            }
        }
    }
    
    public static class EntityEntryExtensions
    {
        public static MemberEntry Member<TEntity>(this EntityEntry<TEntity> entityEntry, Expression<Func<TEntity,object?>> prop)
            where TEntity : class
        {
            var propInfo = GetPropertyInfo(prop);
            MemberEntry memberEntry;
            if (propInfo.PropertyType.IsAssignableTo(typeof(IEnumerable)))
            {
                Expression converted = Expression.Convert(prop.Body, typeof(IEnumerable<object>));
                Expression<Func<TEntity, IEnumerable<object>>> collProp = Expression.Lambda<Func<TEntity, IEnumerable<object>>>(converted, prop.Parameters);
                memberEntry = new(entityEntry.Collection(collProp));
            }
            else
            {
                memberEntry = new(entityEntry.Reference(prop));
            }
            return memberEntry;
        }
        
        private static PropertyInfo GetPropertyInfo<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda)
        {
            Type type = typeof(TSource);
            if (propertyLambda.Body == null)
                throw new ArgumentException(string.Format(
                    "Expression '{0}' refers to a method, not a property.",
                    propertyLambda.ToString()));
            if (propertyLambda.Body is MemberExpression member)
            {
                if (member.Member == null)
                    throw new ArgumentException(string.Format(
                        "Expression '{0}' refers to a field, not a property.",
                        propertyLambda.ToString()));
                if (member.Member is PropertyInfo propInfo)
                {
                    if (type != propInfo.ReflectedType &&
                    !type.IsSubclassOf(propInfo.ReflectedType!))
                        throw new ArgumentException(string.Format(
                            "Expression '{0}' refers to a property that is not from type {1}.",
                            propertyLambda.ToString(),
                            type));
                    else
                        return propInfo;
                }
            }
            throw new ArgumentException(string.Format(
                        "Expression '{0}' doesn't refer to a class property of {1}.",
                        propertyLambda.ToString(),
                    type));
        }
    }
    

    同步:

    this.Entry(myObject).Member(_ => _.MyProperty).Load();
    

    异步:

    await this.Entry(myObject).Member(_ => _.MyProperty).LoadAsync();
    

    【讨论】:

      【解决方案4】:

      您还可以使用 Select 加载引用的集合。

      db.MyObject.Include(x => x.RefObect.Select(y => y.RefRefObject));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-22
        • 1970-01-01
        • 2011-06-22
        • 1970-01-01
        • 1970-01-01
        • 2011-05-09
        • 1970-01-01
        相关资源
        最近更新 更多