通过 wcf 序列化谓词
我有一个 WCF 服务,它公开了一堆返回业务对象的方法。在它的引擎盖下,它有一个很好的存储库层,它使用这样的接口方法:
IEnumerable<User> GetUsers(Func<User, bool> predicate);
因此,如果我希望所有用户都在给定的角色中,我可以这样做:
var plebs = GetUsers(u => u.Roles.Contains(plebRole));
现在我想通过 WCF 接口公开这种任何过滤器都可以满足的想法。 WCF api 需要可供非 .Net 客户端访问,因此我想使用(相对)简单的类型。
我有一个 Filter 对象,它包含属性名称和值:
[DataContract] public class Filter {
[DataMember] public string Property { get; set; }
[DataMember] public string Value { get; set; }
}
所以现在我可以公开这样的 WCF 方法:
IEnumerable<User> GetUsers(IEnumerable<Filter> filters);
然后我可以根据客户端过滤器中的内容构建谓词。现在它变得混乱了:
private static Expression<Func<T, bool>> GetPredicate<T>(Filter filter)
{
var knownPredicates = GetKnownPredicates<T>(filter.Value);
var t = typeof(T);
return knownPredicates.ContainsKey(t) && knownPredicates[t].ContainsKey(filter.Property)
? knownPredicates[t][filter.Property]
: True<T>();
}
private static Dictionary<Type, Dictionary<string, Expression<Func<T, bool>>>> GetKnownPredicates<T>(string value)
{
// ReSharper disable PossibleNullReferenceException
return new Dictionary<Type, Dictionary<string, Expression<Func<T, bool>>>>
{
{
typeof (User), new Dictionary<string, Expression<Func<T, bool>>>
{
{ "Forename", x => (x as User).Forename == value },
{ "IsAdult", x => (x as User).IsAdult.ToString() == value },
...
}
},
{
typeof (Group), new Dictionary<string, Expression<Func<T, bool>>>
{
{ "Name", x => (x as Group).Name == value },
...
}
},
...
};
// ReSharper restore PossibleNullReferenceException
}
在我开始编写 GetKnownPredicates 方法之前,代码并没有真正糟糕。现在确实如此。我该如何修复它?
I have a WCF service that exposes a bunch of methods that return business objects. Under its hood it has a nice repository layer that uses interface methods like this:
IEnumerable<User> GetUsers(Func<User, bool> predicate);
So if I want all users within a given role I can do:
var plebs = GetUsers(u => u.Roles.Contains(plebRole));
Now I want to expose this any-filter-can-be-satisfied thinking over the WCF interface. The WCF api needs to be accessible to non .Net clients so I want to use (relatively) simple types.
I have a Filter object that holds a property name and and value:
[DataContract] public class Filter {
[DataMember] public string Property { get; set; }
[DataMember] public string Value { get; set; }
}
So now I can expose a WCF method like this:
IEnumerable<User> GetUsers(IEnumerable<Filter> filters);
Then I can build predicates based on what comes in, in the clients filters. Now it gets messy:
private static Expression<Func<T, bool>> GetPredicate<T>(Filter filter)
{
var knownPredicates = GetKnownPredicates<T>(filter.Value);
var t = typeof(T);
return knownPredicates.ContainsKey(t) && knownPredicates[t].ContainsKey(filter.Property)
? knownPredicates[t][filter.Property]
: True<T>();
}
private static Dictionary<Type, Dictionary<string, Expression<Func<T, bool>>>> GetKnownPredicates<T>(string value)
{
// ReSharper disable PossibleNullReferenceException
return new Dictionary<Type, Dictionary<string, Expression<Func<T, bool>>>>
{
{
typeof (User), new Dictionary<string, Expression<Func<T, bool>>>
{
{ "Forename", x => (x as User).Forename == value },
{ "IsAdult", x => (x as User).IsAdult.ToString() == value },
...
}
},
{
typeof (Group), new Dictionary<string, Expression<Func<T, bool>>>
{
{ "Name", x => (x as Group).Name == value },
...
}
},
...
};
// ReSharper restore PossibleNullReferenceException
}
Until I started writing the GetKnownPredicates method, the code didn't really stink. Now it does. How do I fix it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您绝对不可能可以通过网络获取您的
Func
。 这个表达式被存储,将被抖动,并将存在于客户端世界中。请记住,您基本上所做的是使用
u => 编译一个匿名客户端函数。 u.Roles.Contains(plebRole)
。因此,您将获得一些用户,稍后您将在客户端对其进行过滤。There is absolutely no way you could get your
Func<User, bool>
across the wire. This expression is stored and will be jitted and will live in the client side world.Remember, what you are basically doing is to compile an anonymous client-side function by using
u => u.Roles.Contains(plebRole)
. So you would be getting some users that you will filter then later at client side.如果你想变得超级花哨,你可以使用 系统.Linq.Expressions.Expression 类,用于根据传入的过滤器动态构建谓词。您知道要搜索的类型,因此您所需要做的就是 使用 Filter.Property 创建属性表达式,然后使用 Filter.Value 创建常量表达式。使用它们组成一个相等的表达式,您就接近终点线了。
习惯编写表达式可能会很痛苦,但是调试器确实很有帮助,并且会在您编写表达式时向您显示表达式的代码,因此请深入尝试!
If you want to be super-fancy you could use the System.Linq.Expressions.Expression class to dynamically build a predicate based on the passed-in filter. You know the type that you're going to search, so all you need to do is to create a property expression using Filter.Property and then a constant expression with Filter.Value. Use them to compose an equal expression and you're near the finish line.
Getting used to composing expressions can be a pain, but the debugger is really helpful and will show you the code for your expression as you compose it, so dive in and try it out!