gpt4 book ai didi

c# - 在已编译查询中重用现有的 linq 表达式

转载 作者:太空宇宙 更新时间:2023-11-03 13:22:37 25 4
gpt4 key购买 nike

考虑以下提供两种方法的代码:一种返回 IQueryable,另一种利用编译查询非常有效地返回与特定 ID 匹配的位置:

    public IQueryable<Location> GetAllLocations()
{
return from location in Context.Location
where location.DeletedDate == null
&& location.Field1 = false
&& location.Field2 = true
&& location.Field3 > 5
select new LocationDto
{
Id = location.Id,
Name = location.Name
}
}


private static Func<MyDataContext, int, Location> _getByIdCompiled;

public Location GetLocationById(int locationId)
{
if (_getByIdCompiled == null) // precompile the query if needed
{
_getByIdCompiled = CompiledQuery.Compile<MyDataContext, int, Location>((context, id) =>

(from location in Context.Location
where location.DeletedDate == null
&& location.Field1 = false
&& location.Field2 = true
&& location.Field3 > 5
&& location.Id == id
select new LocationDto {
Id = location.Id,
Name = location.Name
})).First());
}

// Context is a public property on the repository all of this lives in
return _getByIdCompiled(Context, locationId);
}

这是对实际代码的相当大的简化,但我认为它理解了这个想法,并且工作正常。接下来我要做的是重构代码,以便可以重用表达式的公共(public)位,因为它将用于许多其他类型的编译查询。换句话说,这个表达式:

                from location in Context.Location
where location.DeletedDate == null
&& location.Field1 = false
&& location.Field2 = true
&& location.Field3 > 5
select new LocationDto
{
Id = location.Id,
Name = location.Name
};

我怎样才能以某种方式在变量或函数中捕获它并在多个编译查询中重用它?到目前为止,我的尝试导致错误提示无法翻译成 SQL、不允许成员访问等。

更新:我提出这个问题的另一种可能更好的方法如下:

考虑下面的两个编译查询:

_getByIdCompiled = CompiledQuery.Compile<MyDataContext, int, LocationDto>((context, id) => 
(from location in Context.Location // here
where location.DeletedDate == null // here
&& location.Field1 = false // here
&& location.Field2 = true // here
&& location.Field3 > 5 // here
&& location.Id == id
select new LocationDto { // here
Id = location.Id, // here
Name = location.Name
})).First()); // here

_getByNameCompiled = CompiledQuery.Compile<MyDataContext, int, LocationDto>((context, name) =>
(from location in Context.Location // here
where location.DeletedDate == null // here
&& location.Field1 = false // here
&& location.Field2 = true // here
&& location.Field3 > 5 // here
&& location.Name == name
select new LocationDto { // here
Id = location.Id, // here
Name = location.Name // here
})).First()); // here

所有标记为//here 的行都是重复的非常不干的代码片段。 (在我的代码库中,这实际上有 30 多行代码。)我如何将其提取出来并使其可重用?

最佳答案

因此,整个事情有点奇怪,因为 Compile 方法不仅需要查看传递给每个查询运算符的 Expression 对象(WhereSelect 等)作为它可以理解的东西,但它需要看到整个查询,包括所有运算符的使用,作为它可以理解为 Expression 对象。这几乎移除了更传统的查询组合选项。

这会变得有点困惑;比我真正想要的要多,但我没有看到很多很棒的选择。

我们要做的是创建一个方法来构造我们的查询。它将接受一个 filter 作为参数,该过滤器将是一个 Expression,代表某个对象的过滤器。

接下来我们将定义一个 lambda,它看起来几乎与您将传递给 Compile 的内容完全相同,但带有一个额外的参数。这个额外的参数将与我们的过滤器具有相同的类型,并且它将代表那个实际的过滤器。我们将在整个 lambda 中使用该参数而不是过滤器。然后,我们将使用 UseIn 方法将新 lambda 中第三个参数的所有实例替换为我们提供给该方法的 filter 表达式。

构造查询的方法如下:

private static Expression<Func<MyDataContext, int, IQueryable<LocationDto>>>
ConstructQuery(Expression<Func<Location, bool>> filter)
{
return filter.UseIn((MyDataContext context, int id,
Expression<Func<Location, bool>> predicate) =>
from location in context.Location.Where(predicate)
where location.DeletedDate == null
&& location.Field1 == false
&& location.Field2 == true
&& location.Field3 > 5
&& location.Id == id
select new LocationDto
{
Id = location.Id,
Name = location.Name
});
}

这是 UseIn 方法:

public static Expression<Func<T3, T4, T5>>
UseIn<T1, T2, T3, T4, T5>(
this Expression<Func<T1, T2>> first,
Expression<Func<T3, T4, Expression<Func<T1, T2>>, T5>> second)
{
return Expression.Lambda<Func<T3, T4, T5>>(
second.Body.Replace(second.Parameters[2], first),
second.Parameters[0],
second.Parameters[1]);
}

(这里的输入很乱,我不知道如何给泛型类型赋予有意义的名称。)

以下方法用于将一个表达式的所有实例替换为另一个表达式:

public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
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);
}
}

既然我们已经解决了这个血腥的困惑局面,那么简单的部分就来了。 ConstructQuery 此时应该能够被修改以表示您的真实 查询,没有太多困难。

要调用此方法,我们只需要提供我们想要应用于此查询更改的任何过滤器,例如:

var constructedQuery = ConstructQuery(location => location.Id == locationId); 

关于c# - 在已编译查询中重用现有的 linq 表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23639675/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com