Entity Framework core为了提供更易理解的API而牺牲了参数设置的便利性。实际上,在EF6中,将多层Include
表达式传递给方法要容易得多。在ef-core中几乎不可能。
但是,仍然存在接受属性路径作为字符串的Include
方法,因此,如果我们可以将旧式的多级Include
表达式转换为路径,就可以将路径馈入这个基于字符串的Include
中。
幸运的是,在EF6中恰好发生了这种情况。由于EF6是开源的,我不必重新发明轮子,而是可以轻松借用他们的代码来实现我们想要的效果。结果是一个扩展方法AsPath
,它返回一个lambda表达式作为属性路径。您可以在方法内部使用它将includes
参数转换为一系列字符串,以添加Include
。例如,表达式...
include => include.PlanSolutions.Select(ps => ps.Solution)
...将被转换为PlanSolutions.Solution
。
如上所述:核心部分的源代码归功于 EF6。唯一的主要修改是,在我方法中,两个最常尝试的不支持特性:在Include
中进行筛选和排序会引发异常。(在 ef-core 中仍不支持)。
public static class ExpressionExtensions
{
public static string AsPath(this LambdaExpression expression)
{
if (expression == null) return null;
var exp = expression.Body;
string path;
TryParsePath(exp, out path);
return path;
}
private static bool TryParsePath(Expression expression, out string path)
{
path = null;
var withoutConvert = RemoveConvert(expression);
var memberExpression = withoutConvert as MemberExpression;
var callExpression = withoutConvert as MethodCallExpression;
if (memberExpression != null)
{
var thisPart = memberExpression.Member.Name;
string parentPart;
if (!TryParsePath(memberExpression.Expression, out parentPart))
{
return false;
}
path = parentPart == null ? thisPart : (parentPart + "." + thisPart);
}
else if (callExpression != null)
{
if (callExpression.Method.Name == "Select"
&& callExpression.Arguments.Count == 2)
{
string parentPart;
if (!TryParsePath(callExpression.Arguments[0], out parentPart))
{
return false;
}
if (parentPart != null)
{
var subExpression = callExpression.Arguments[1] as LambdaExpression;
if (subExpression != null)
{
string thisPart;
if (!TryParsePath(subExpression.Body, out thisPart))
{
return false;
}
if (thisPart != null)
{
path = parentPart + "." + thisPart;
return true;
}
}
}
}
else if (callExpression.Method.Name == "Where")
{
throw new NotSupportedException("Filtering an Include expression is not supported");
}
else if (callExpression.Method.Name == "OrderBy" || callExpression.Method.Name == "OrderByDescending")
{
throw new NotSupportedException("Ordering an Include expression is not supported");
}
return false;
}
return true;
}
private static Expression RemoveConvert(Expression expression)
{
while (expression.NodeType == ExpressionType.Convert
|| expression.NodeType == ExpressionType.ConvertChecked)
{
expression = ((UnaryExpression)expression).Operand;
}
return expression;
}
}
FirstOrDefault
实现中将所有的Select
重写为ThenInclude
。 - EvkInclude("path string")
。 - Evk