我想使用Expression<Func<>>
方法来清理我的DTO Linq-to-Query Selects,以免它们比现有的增长。关于如何将它们集成到我自己的项目中,我仍然有点困惑。
一个例子:
public IQueryable<ExampleDTO> SelectDTO()
{
var repository = new ExampleUDCRepository();
return db.Example
.Select(v => new ExampleDTO
{
ExampleID = v.ExampleID,
MasterGroupID = v.MasterGroupID,
//...etc
ExampleUDCs = db.ExampleUDCs
.Where(vudc => vudc.ExampleID == v.ExampleID)
.AsEnumerable()
.Select(vudc => new ExampleDCDTO
{
ExampleID = vudc.ExampleID,
UDCHeadingID = vudc.UDCHeadingID,
UDCValue = vudc.UDCValue
})
});
我的一些其他DTO设置和返回方法甚至更大,更笨拙。
我真正想做的是这样的事情:
public IQueryable<ExampleDTO> SelectDTO()
{
var repository = new ExampleUDCRepository();
return db.Example
.Select(v => new ExampleDTO
{
ExampleID = v.ExampleID,
MasterGroupID = v.MasterGroupID,
//...etc
ExampleUDCs = new ExampleUDCsRepository().SelectDTO(v);
// SelectDTO(Example v) in that repository would call
// any other SelectDTO it might need and so forth
});
问题是,Linq不知道如何将这样的方法转换为SQL语句,这是Expression<Func<>>
方法在传递这类事物方面发挥作用的地方。
我对Expression<Func<>>
的理解是有限的,我还没有找到任何文件,这使得我正在努力做得更清楚。
将Expression<Func<>>
整合到我的DTO中的最佳方法是什么?
答案 0 :(得分:2)
首先,我们需要一些辅助方法。我们将从这个简单的类开始,将一个表达式的所有实例替换为另一个:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
接下来,我们将创建一个扩展方法来使用它:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
最后,我们将创建一个将两个表达式组合在一起的Combine
方法。它将采用一个表达式来计算一个值的中间结果,然后另一个表达式使用第一个值和中间结果来确定最终结果。
public static Expression<Func<TFirstParam, TResult>>
Combine<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], param)
.Replace(second.Parameters[1], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
接下来,我们可以定义在给定示例对象的情况下计算ExampleDCDTO对象的方法。它将直接提取您的上述内容,但不是返回IEnumerable<ExampleDCDTO>
,而是需要返回一个将Example
转换为这样一个序列的表达式:
public Expression<Func<Example, IEnumerable<ExampleDCDTO>>> SelectDTO()
{
return v => db.ExampleUDCs.Where(vudc => vudc.ExampleID == v.ExampleID)
.AsEnumerable()
.Select(vudc => new ExampleDCDTO
{
ExampleID = vudc.ExampleID,
UDCHeadingID = vudc.UDCHeadingID,
UDCValue = vudc.UDCValue
});
}
现在要把它们放在一起,我们可以调用这个SelectDTO
方法来生成计算中间值的表达式,并使用另一个使用它的表达式Combine
:
public IQueryable<ExampleDTO> SelectDTO()
{
ExampleUDCRepository repository = new ExampleUDCRepository();
return db.Example
.Select(repository.SelectDTO().Combine((v, exampleUDCs) =>
new ExampleDTO()
{
ExampleID = v.ExampleID,
MasterGroupID = v.MasterGroupID,
ExampleUDCs = exampleUDCs,
}));
}
对于那些使用LINQKit的人来说,另一个选择是使用AsExpandable
而不是所有的帮助方法。使用这种方法仍然需要创建返回SelectDTO
的{{1}}方法,但您可以将结果组合起来:
Expression<Func<Example, IEnumerable<ExampleDCDTO>>>