我遇到了 OData、
IQueryable
以及从 LINQ 语句调用的映射函数的问题。
这是一个函数(创建一个
IQueryable
):
IQueryable<AvailableView> queryable =
_aContext.AView
.FromSqlRaw(sql).AsNoTracking()
.OrderBy(linqOrderByClause)
.Select(x => new AvailableView()
{
Afield1 = x.Afield1
Afield2 = ConvertSomething(x.Afield2)
});
这是映射的静态函数
ConvertSomething
(好吧,不是真的,我只是想表明这个函数可以像这样简单):
private static string ConvertSomething(string Afield2)
{
return Afield2;
}
这个
IQueryable
是在我们创建的一些“分页响应”类中执行的,当需要执行IQueryable
时,这里是一些代码:
ODataQuerySettings settings = new ODataQuerySettings();
// ODataQueryOptions options is passed into this function
if (options != null)
{
if (options.Filter != null)
{
queryable = options.Filter.ApplyTo(queryable, settings) as IQueryable<dynamic>;
}
if (options.OrderBy != null)
{
queryable = options.OrderBy.ApplyTo(queryable, settings) as IQueryable<dynamic>;
}
if (options.SelectExpand != null)
{
queryable = options.SelectExpand.ApplyTo(queryable, settings) as IQueryable<dynamic>;
}
}
List<dynamic> dataPage = queryable
.Skip(skip)
.Take(take)
.ToList(); <---- this is where I run into the error
当我们执行代码时 - 没有问题。
当我们添加OData时,例如:
"$filter=tolower(Afield2) eq 'hey'"
当我们执行
ToList()
时它就会爆炸。我可以将相同的过滤器应用于Afield1
,没问题,只是不是Afield2
。这是因为我们正在使用静态映射函数。
错误消息的内容如下:
System.InvalidOperationException:LINQ 表达式 'DbSet().FromSql(我们的 sql 语句), __p_0) .OrderBy(a => a.SomeField) .ThenBy(a => a.SomeField) .ThenBy(a => a.AnotherField) .ThenBy(a => a.OneMoreField) .Where(a => (string)OurProject.ConvertSomething(a.AField2).ToLower() == __TypedProperty_3)' 无法翻译。
附加信息:
方法“blah.blah.blah.ConvertSomething”的翻译失败。 如果此方法可以映射到您的自定义函数,请参阅 https://go.microsoft.com/fwlink/?linkid=2132413 了解更多信息。 以可翻译的形式重写查询,或者通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用来显式切换到客户端计算。 请参阅 https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。
使用 EF
IQueryable
和延迟执行时,使用的任何/所有表达式都需要能够转换为 SQL。提供者将支持一些框架扩展方法和特定于类型的方法(例如System.DateTime
下的那些),但他们显然不会理解自定义扩展方法或其他自定义函数。
最简单的解决方案,尤其是当它应用于投影时,是将翻译责任下移到投影中。 (视图模型)
public class AvailableView
{
public int Afield1 { get; set; }
public string RawAfield2 { get; set; }
public string Afield2 => ConvertSomething(RawAfield2);
}
.Select(x => new AvailableView()
{
Afield1 = x.Afield1
RawAfield2 = x.Afield2
});
转换可以是视图模型下的私有静态方法、静态单例辅助类等。使用视图将使用 Afield2 作为经过验证/格式化的值。