If you want to continue to support property expressions so that you can support "Include" syntax FindAsync, the following solution retrieves the PropertyInfo for the referred property then uses Expression.Convert
so that you can now support the context.Entry(entity).Member(e => e.Property).Load()
syntax you wished.
Add the following two classes to some namespace and using
it in your class:
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));
}
}
Synchronous:
this.Entry(myObject).Member(_ => _.MyProperty).Load();
Asynchronous:
await this.Entry(myObject).Member(_ => _.MyProperty).LoadAsync();
context.Entry(entity).Collection("collection").Load()
method? – Melanimelania