![](/img/trans.png)
[英]Calling the .Any Extension Method with Entity Framework using Expression Trees
[英]Is it possible to convince the Entity Framework to make an expression when calling a method from a select?
我正在使用Entity Framework 6(POCO,代码优先方法),并且遇到以下情况...
我有一个实体,该实体的属性是枚举(称为状态)-数据库中的值是整数,我想向用户显示此值的本地化描述。
因此,我的代码看起来像这样(实际上,它调用ResourceManager
并具有更多状态值,但这只是指示性的):
public static string ToDisplayable(Status status)
{
switch(status)
{
case Status.One:
return "Status ONE";
case Status.Two:
return "Status TWO";
default:
return "Unknown Status";
}
}
...
var results = someIqueryable
.ToList()
.Select(r => new
{
Status = r.Status,
DisplayableStatus = ToDisplayable(r.Status)
});
现在,这很好,因为toList
当然很好,这意味着在调用Select
之前,我所有的东西都已从数据库中撤回。 如果删除ToList
我当然会得到以下例外:
mscorlib.dll中发生类型'System.NotSupportedException'的异常,但未在用户代码中处理
附加信息:LINQ to Entities无法识别方法'System.String ToDisplayable(Status)',并且该方法无法转换为商店表达式。
好吧,很公平,它不能将我的函数调用转换为SQL,也不能真正怪我。
事情是我真的,真的,真的不想说ToList
在那里,因为后来一些Where
条款可能(也可能不会)被添加,此外,我只是不需要对我的实体中的所有列。
因此,我认为我唯一的选择是这样做:
var results = someIqueryable
.Select(r => new
{
Status = r.Status,
DisplayableStatus = r.Status == Status.One
? "Status ONE"
: r.Status == Status.Two
? "Status TWO"
: "Unknown Status"
});
这有点丑陋和乏味(在我的实际代码中大约有15个状态值),最糟糕的是,我无法重用它(但它至少可以工作)。
一开始,简单方法的优点在于,因为还有其他带有“状态”列的实体,它们具有相同的可能值,因此我可以调用同一方法。 现在,例如,如果我添加一个新的状态值,我必须遍历我的代码以查找进行此翻译的所有位置。 可怕。
因此,有人知道我如何创建可重用的代码段,这些代码段可翻译为可插入Select
方法的SQL表达式吗?
我可以用一种Expression
编织一些魔术吗?
将枚举值转换为可读文本不是显示代码应注意的事情吗? 在将数据绑定到View之前,返回Enum并将Enumeration转换为可显示的值。
在最坏的情况下,您始终可以执行以下操作:
var results = someIqueryable
.Where(...)
.Select(r => new Status = r.Status, /* other column you're after */)
.ToList()
.Select(r => new
{
Status = r.Status,
/* other column you're after */,
DisplayableStatus = ToDisplayable(r.Status)
});
我想出了一种方法。 LinqKit项目正是我需要的,甚至还有一个方便的Nuget包,所以我做到了:
Install-Package LinqKit
我认为我正在使用的该库中的代码最初来自Tomas Petricek 。 我希望它在主要的Entity Framework项目中,但是无论如何...
现在,我已引用该库,我可以创建一个助手:
public static class StatusHelper
{
public static readonly Expression<Func<Status, string>> ToDisplayable = s =>
s == Status.One
? "Status ONE"
: s == Status.Two
? "Status TWO"
: "Unknown Status";
}
然后像这样使用该助手:
// It is important to assign to a local variable here and
// not attempt to use the StatusHelper in the query
// see - http://stackoverflow.com/q/22223944/1039947
// Not ideal, of course, but I can live with it
var toDisplayable = StatusHelper.ToDisplayable;
var results = someIqueryable
.AsExpandable() // <-- This is needed for LinqKit
.Select(x => new
{
Status = x.Status,
DisplayableStatus = toDisplayable.Invoke(x.Status)
})
.ToList();
如您所见,我可以在Select中 调用表达式,并且在对数据库的查询中发生了正确的事情。
这样,我可以轻松地分享我的小方法。 因此,我是一个快乐的兔子,正如您所看到的,我也不需要尽早评估查询。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.