如果您想继续支持属性表达式以便可以支持“包含”语法 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();