通过 wcf 序列化谓词

发布于 2024-10-05 10:36:01 字数 2010 浏览 7 评论 0原文

我有一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半山落雨半山空 2024-10-12 10:36:02

您绝对不可能可以通过网络获取您的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.

假面具 2024-10-12 10:36:01

如果你想变得超级花哨,你可以使用 系统.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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文