gpt4 book ai didi

linq - 如何分配 IQueryable 的属性值?

转载 作者:行者123 更新时间:2023-12-04 18:51:27 25 4
gpt4 key购买 nike

我正在使用 Entity Framework 4.1 Code First。在我的实体中,我有三个日期/时间属性:

public class MyEntity
{
[Key]
public Id { get; set; }

public DateTime FromDate { get; set; }

public DateTime ToDate { get; set; }

[NotMapped]
public DateTime? QueryDate { get; set; }

// and some other fields, of course
}

在数据库中,我总是填充从/到日期。我使用一个简单的 where 子句查询它们。但在结果集中,我想包含我查询的日期。我需要坚持这一点,以使其他一些业务逻辑正常工作。

我正在研究一种扩展方法来做到这一点,但我遇到了问题:
public static IQueryable<T> WhereDateInRange<T>(this IQueryable<T> queryable, DateTime queryDate) where T : MyEntity
{
// this part works fine
var newQueryable = queryable.Where(e => e.FromDate <= queryDate &&
e.ToDate >= queryDate);

// in theory, this is what I want to do
newQueryable = newQueryable.Select(e =>
{
e.QueryDate = queryDate;
return e;
});
return newQueryable;
}

这行不通。如果我使用 IEnumerable,它可以工作,但我想将其保留为 IQueryable,以便一切都在数据库端运行,并且这种扩展方法仍然可以用于另一个查询的任何部分。当它是 IQueryable 时,我收到以下编译错误:

A lambda expression with a statement body cannot be converted to an expression tree



如果这是 SQL,我会做这样的事情:
SELECT *, @QueryDate as QueryDate
FROM MyEntities
WHERE @QueryDate BETWEEN FromDate AND ToDate

所以问题是,如何转换已经必须包含这个额外属性分配的表达式树?我研究了 IQueryable.Expression 和 IQueryable.Provider.CreateQuery - 那里有一个解决方案。也许可以将赋值表达式附加到现有的表达式树?我对表达式树方法不够熟悉,无法弄清楚这一点。有任何想法吗?

示例用法

澄清一下,目标是能够执行以下操作:
var entity = dataContext.Set<MyEntity>()
.WhereDateInRange(DateTime.Now)
.FirstOrDefault();

并将 DateTime.Now 保留到结果行的 QueryDate 中,而不会从数据库查询返回多于一行。 (使用 IEnumerable 解决方案,在 FirstOrDefault 选择我们想要的行之前返回多行。)

另一个想法

我可以继续将 QueryDate 映射为真实字段,并将其 DatabaseGeneratedOption 设置为 Computed。但是我需要一些方法将“@QueryDate as QueryDate”注入(inject)到由 EF 的 select 语句创建的 SQL 中。由于它是计算出来的,EF 不会在更新或插入期间尝试提供值。那么我该如何将自定义 SQL 注入(inject)到 select 语句中呢?

最佳答案

拉迪斯拉夫是绝对正确的。但是,由于您显然希望回答问题的第二部分,因此您可以使用 Assign。不过,这不适用于 EF。

using System;
using System.Linq;
using System.Linq.Expressions;

namespace SO5639951
{
static class Program
{
static void Main()
{
AdventureWorks2008Entities c = new AdventureWorks2008Entities();
var data = c.Addresses.Select(p => p);

ParameterExpression value = Expression.Parameter(typeof(Address), "value");
ParameterExpression result = Expression.Parameter(typeof(Address), "result");
BlockExpression block = Expression.Block(
new[] { result },
Expression.Assign(Expression.Property(value, "AddressLine1"), Expression.Constant("X")),
Expression.Assign(result, value)
);

LambdaExpression lambdaExpression = Expression.Lambda<Func<Address, Address>>(block, value);

MethodCallExpression methodCallExpression =
Expression.Call(
typeof(Queryable),
"Select",
new[]{ typeof(Address),typeof(Address) } ,
new[] { data.Expression, Expression.Quote(lambdaExpression) });

var data2 = data.Provider.CreateQuery<Address>(methodCallExpression);

string result1 = data.ToList()[0].AddressLine1;
string result2 = data2.ToList()[0].AddressLine1;
}
}
}
更新 1
这是经过一些调整后的相同代码。我摆脱了 EF 在上面的代码中阻塞的“Block”表达式,以绝对清晰地证明它是 EF 不支持的“Assign”表达式。请注意,Assign 原则上适用于通用表达式树,它是不支持 Assign 的 EF 提供程序。
using System;
using System.Linq;
using System.Linq.Expressions;

namespace SO5639951
{
static class Program
{
static void Main()
{
AdventureWorks2008Entities c = new AdventureWorks2008Entities();

IQueryable<Address> originalData = c.Addresses.AsQueryable();

Type anonType = new { a = new Address(), b = "" }.GetType();

ParameterExpression assignParameter = Expression.Parameter(typeof(Address), "value");
var assignExpression = Expression.New(
anonType.GetConstructor(new[] { typeof(Address), typeof(string) }),
assignParameter,
Expression.Assign(Expression.Property(assignParameter, "AddressLine1"), Expression.Constant("X")));
LambdaExpression lambdaAssignExpression = Expression.Lambda(assignExpression, assignParameter);

var assignData = originalData.Provider.CreateQuery(CreateSelectMethodCall(originalData, lambdaAssignExpression));
ParameterExpression selectParameter = Expression.Parameter(anonType, "value");
var selectExpression = Expression.Property(selectParameter, "a");
LambdaExpression lambdaSelectExpression = Expression.Lambda(selectExpression, selectParameter);

IQueryable<Address> finalData = assignData.Provider.CreateQuery<Address>(CreateSelectMethodCall(assignData, lambdaSelectExpression));

string result = finalData.ToList()[0].AddressLine1;
}

static MethodCallExpression CreateSelectMethodCall(IQueryable query, LambdaExpression expression)
{
Type[] typeArgs = new[] { query.ElementType, expression.Body.Type };
return Expression.Call(
typeof(Queryable),
"Select",
typeArgs,
new[] { query.Expression, Expression.Quote(expression) });

}
}
}

关于linq - 如何分配 IQueryable<T> 的属性值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5639951/

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