14

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

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

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

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

编辑

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

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

例如

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();
4

4 回答 4

18

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

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

这就是为什么您必须在和方法之间显式选择的原因Collection,每个方法都公开不同的功能集(因此返回不同的类型)。ReferenceProperty

例如:

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

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

  • Reference Collection ( 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();
于 2013-08-06T08:01:50.370 回答
4

你可以这样做:

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

于 2014-10-30T10:03:03.873 回答
1

如果您想继续支持属性表达式以便可以支持“包含”语法 FindAsync,以下解决方案会检索引用属性的 PropertyInfo,然后使用Expression.Convert,以便您现在可以支持context.Entry(entity).Member(e => 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();
于 2022-02-18T05:02:48.370 回答
0

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

db.MyObject.Include(x => x.RefObect.Select(y => y.RefRefObject));
于 2019-05-31T16:49:43.547 回答