【发布时间】:2013-08-28 12:40:00
【问题描述】:
注意:这是一篇很长的帖子,请滚动到底部查看问题 - 希望这会让我更容易理解我的问题。谢谢!
我有“会员”模型,定义如下:
public class Member
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string ScreenName { get; set; }
[NotMapped]
public string RealName
{
get { return (FirstName + " " + LastName).TrimEnd(); }
}
[NotMapped]
public string DisplayName
{
get
{
return string.IsNullOrEmpty(ScreenName) ? RealName : ScreenName;
}
}
}
这是现有的项目和模型,我不想更改它。现在我们收到了一个通过 DisplayName 启用配置文件检索的请求:
public Member GetMemberByDisplayName(string displayName)
{
var member = this.memberRepository
.FirstOrDefault(m => m.DisplayName == displayName);
return member;
}
此代码不起作用,因为 DisplayName 未映射到数据库中的字段。好,那我就做个表态:
public Member GetMemberByDisplayName(string displayName)
{
Expression<Func<Member, bool>> displayNameSearchExpr = m => (
string.IsNullOrEmpty(m.ScreenName)
? (m.Name + " " + m.LastName).TrimEnd()
: m.ScreenName
) == displayName;
var member = this.memberRepository
.FirstOrDefault(displayNameSearchExpr);
return member;
}
这行得通。唯一的问题是生成显示名称的业务逻辑被复制/粘贴在 2 个不同的地方。我想避免这种情况。但我不明白该怎么做。我带来的最好的是:
public class Member
{
public static Expression<Func<Member, string>> GetDisplayNameExpression()
{
return m => (
string.IsNullOrEmpty(m.ScreenName)
? (m.Name + " " + m.LastName).TrimEnd()
: m.ScreenName
);
}
public static Expression<Func<Member, bool>> FilterMemberByDisplayNameExpression(string displayName)
{
return m => (
string.IsNullOrEmpty(m.ScreenName)
? (m.Name + " " + m.LastName).TrimEnd()
: m.ScreenName
) == displayName;
}
private static readonly Func<Member, string> GetDisplayNameExpressionCompiled = GetDisplayNameExpression().Compile();
[NotMapped]
public string DisplayName
{
get
{
return GetDisplayNameExpressionCompiled(this);
}
}
[NotMapped]
public string RealName
{
get { return (FirstName + " " + LastName).TrimEnd(); }
}
}
问题:
(1)如何在FilterMemberByDisplayNameExpression()中重用GetDisplayNameExpression()?我试过Expression.Invoke:
public static Expression<Func<Member, bool>> FilterMemberByDisplayNameExpression(string displayName)
{
Expression<Func<string, bool>> e0 = s => s == displayName;
var e1 = GetDisplayNameExpression();
var combinedExpression = Expression.Lambda<Func<Member, bool>>(
Expression.Invoke(e0, e1.Body), e1.Parameters);
return combinedExpression;
}
但我从提供商那里收到以下错误:
LINQ 不支持 LINQ 表达式节点类型“Invoke” 实体。
(2) 在 DisplayName 属性中使用 Expression.Compile() 是一种好方法吗?有什么问题吗?
(3) 如何在 GetDisplayNameExpression() 中移动 RealName 逻辑?我想我必须创建另一个表达式和另一个编译表达式,但我不明白如何从 GetDisplayNameExpression() 内部调用 RealNameExpression。
谢谢。
【问题讨论】:
-
A) 没有好的解决方案。 B) 你所做的是一场噩梦(不是因为你不知道自己在做什么,而是因为
FilterMemberByDisplayNameExpression是一场等待你做梦的噩梦)。 C) 没有好的解决方案。 -
@xanatos 为什么 (B) 是一场噩梦?你能解释一下最大的问题是什么吗?
-
如果你真的想做某事,你可以像这样创建一个“过滤器”:
static IQueryable<T> MakeMyExpressionWork(this IQueryable<T> exp)。这个魔法过滤器,通过表达式重写、反射、mumbo jumbo 等会修改Expression。 -
@xanatos 您所说的对于 (A) 和 (C) 情况是正确的 - 我知道我需要以某种方式修改表达式,这是我的问题。但是我对(B)感到困惑-编译表达式并在属性中使用它有什么问题,只是为了避免业务逻辑复制/粘贴?
-
如果你有 Sql Server 作为数据库,你介意使用计算列吗?这将使这类事情变得非常容易(我们不久前遇到过同样的问题)!
标签: c# linq entity-framework lambda expression