我在泛型类中有以下情况:
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.TimeZone
和Region.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; }
}
}