正如其他人在comments 中所说,您可以使用EF6 code 解析您的表达式并应用相关的Include/ThenInclude 调用。毕竟它看起来并不难,但由于这不是我的想法,我宁愿不要用代码给出答案。
您可以改为更改您的模式以公开某些接口,允许您从调用者指定您的包含,而不让它访问底层可查询。
这将导致类似:
using YourProject.ExtensionNamespace;
// ...
patientRepository.GetById(0, ip => ip
.Include(p => p.Addresses)
.ThenInclude(a=> a.Country));
命名空间上的using 必须与包含在最后一个代码块中定义的扩展方法的命名空间名称匹配。
GetById 现在是:
public static Patient GetById(int id,
Func<IIncludable<Patient>, IIncludable> includes)
{
return context.Patients
.IncludeMultiple(includes)
.FirstOrDefault(x => x.EndDayID == id);
}
扩展方法IncludeMultiple:
public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query,
Func<IIncludable<T>, IIncludable> includes)
where T : class
{
if (includes == null)
return query;
var includable = (Includable<T>)includes(new Includable<T>(query));
return includable.Input;
}
Includable 类和接口,它们是简单的“占位符”,附加扩展方法将在其上完成模仿 EF Include 和 ThenInclude 方法的工作:
public interface IIncludable { }
public interface IIncludable<out TEntity> : IIncludable { }
public interface IIncludable<out TEntity, out TProperty> : IIncludable<TEntity> { }
internal class Includable<TEntity> : IIncludable<TEntity> where TEntity : class
{
internal IQueryable<TEntity> Input { get; }
internal Includable(IQueryable<TEntity> queryable)
{
// C# 7 syntax, just rewrite it "old style" if you do not have Visual Studio 2017
Input = queryable ?? throw new ArgumentNullException(nameof(queryable));
}
}
internal class Includable<TEntity, TProperty> :
Includable<TEntity>, IIncludable<TEntity, TProperty>
where TEntity : class
{
internal IIncludableQueryable<TEntity, TProperty> IncludableInput { get; }
internal Includable(IIncludableQueryable<TEntity, TProperty> queryable) :
base(queryable)
{
IncludableInput = queryable;
}
}
IIncludable 扩展方法:
using Microsoft.EntityFrameworkCore;
// others using ommitted
namespace YourProject.ExtensionNamespace
{
public static class IncludableExtensions
{
public static IIncludable<TEntity, TProperty> Include<TEntity, TProperty>(
this IIncludable<TEntity> includes,
Expression<Func<TEntity, TProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity>)includes).Input
.Include(propertySelector);
return new Includable<TEntity, TProperty>(result);
}
public static IIncludable<TEntity, TOtherProperty>
ThenInclude<TEntity, TOtherProperty, TProperty>(
this IIncludable<TEntity, TProperty> includes,
Expression<Func<TProperty, TOtherProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity, TProperty>)includes)
.IncludableInput.ThenInclude(propertySelector);
return new Includable<TEntity, TOtherProperty>(result);
}
public static IIncludable<TEntity, TOtherProperty>
ThenInclude<TEntity, TOtherProperty, TProperty>(
this IIncludable<TEntity, IEnumerable<TProperty>> includes,
Expression<Func<TProperty, TOtherProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity, IEnumerable<TProperty>>)includes)
.IncludableInput.ThenInclude(propertySelector);
return new Includable<TEntity, TOtherProperty>(result);
}
}
}
IIncludable<TEntity, TProperty> 几乎类似于 EF 中的 IIncludableQueryable<TEntity, TProperty>,但它不扩展 IQueryable 并且不允许重新调整查询。
当然,如果调用者在同一个程序集中,它仍然可以将IIncludable 转换为Includable 并开始摆弄可查询对象。但是好吧,如果有人想弄错,我们没有办法阻止他这样做(反射允许任何事情)。重要的是暴露的合同。
现在,如果您不关心将 IQueryable 暴露给调用者(我对此表示怀疑),显然只需将您的 params 参数更改为 Func<Queryable<T>, Queryable<T>> addIncludes 参数,并避免对上述所有内容进行编码。
最后最好:我没有测试过这个,我目前不使用实体框架!