如何使用表达式在 LINQ 中选择"新建"分配属性



我在泛型类中有以下情况:

IQueryable<TQuote> query = GetQuotes(ctx)

其中ctx是数据库上下文,GetQuotes返回DbSet<TQuote>

然后在代码的某个地方执行查询。其简化形式如下:

var list = await query
.Select(v => new
{
v.Id,
TimeZone = v.Property != null ? (int?)v.Property.TimeZone : null
// and about 10 more simple properties like v.Id above
}
.ToListAsync();

其中Property是导航属性(到另一个表),TimeZone只是 C# 中int?类型的常规数据库列/属性。

一切都有效,直到我尝试将该泛型类与实体一起使用,该实体没有导航属性Property。因此,我想用类的抽象成员替换v.Property != null ? (int?)v.Property.TimeZone : null的"硬编码"表达式,然后针对不同的TQuote类型以不同的方式覆盖它。

我尝试了这个签名:

protected abstract Expression<Func<TQuote, int?>> GetTimeZone();

但是如果我在 LINQ 中使用它(直接或先分配给某个变量),那么TimeZone的签名也会更改为Expression<...>,如果我尝试...Compile().Invoke(v),那么 LINQ 会抱怨 LINQ to 实体不支持这一点。

我看到了这个答案: 创建一个动态 Linq to EF 表达式以选择 IQueryable 到新类并分配属性 但是,它手动构建了整个选择器,并且鉴于我总共有 16 个属性,创建和维护这些属性会很痛苦。所以,我想知道我是否可以只对这个TimeZone做点什么,但其余的都保留在简单的 LINQ 形式中,如上所示。

是否可能,如果是,那么究竟如何?

试试 nuget LINQKit,https://github.com/scottksmith95/LINQKit。

以下是我的尝试。 LinqKit 提供了AsExpandable()Invoke(v)方法。 我编造了像Area.TimeZoneRegion.RegionalTimeZone这样的属性。

using LinqKit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
class QuoteResult
{
public int Id { get; set; }
public int? TimeZone { get; set; }
public string Note { get; set; }
}
abstract class QuoteHelper<TQuote> where TQuote : Quote
{
protected abstract Expression<Func<TQuote, int?>> GetTimeZone { get; }
public IEnumerable<QuoteResult> GetQuoteResults(
EfExpressionPropertyDbContext ctx)
{
IQueryable<TQuote> query = GetQuotes(ctx);
var getTimeZone = GetTimeZone;
var list = query
.AsExpandable()
.Select(v => new QuoteResult
{
Id = v.Id,
TimeZone = getTimeZone.Invoke(v),
Note = v.Note
// and about 10 more simple properties like v.Id above
})
.ToList();
return list;
}
public IQueryable<TQuote> GetQuotes(
EfExpressionPropertyDbContext ctx)
{
return ctx.Set<TQuote>();
}
}
class CommonQuoteHelper : QuoteHelper<CommonQuote>
{
protected override Expression<Func<CommonQuote, int?>> GetTimeZone
{
get { return q => q.Area != null ? (int?)q.Area.TimeZone : null; }
}
}
class PeculiarQuoteHelper : QuoteHelper<PeculiarQuote>
{
protected override Expression<Func<PeculiarQuote, int?>> GetTimeZone
{
get { return q => q.Region != null ? (int?)q.Region.RegionalTimeZone : null; }
}
}

最新更新