Сериализация предикатов над wcf - PullRequest
3 голосов
/ 02 декабря 2010

У меня есть служба WCF, которая предоставляет набор методов, которые возвращают бизнес-объекты. Под капотом у него есть хороший слой репозитория, который использует такие методы интерфейса:

IEnumerable<User> GetUsers(Func<User, bool> predicate);

Итак, если я хочу, чтобы все пользователи в рамках данной роли, я мог сделать:

var plebs = GetUsers(u => u.Roles.Contains(plebRole));

Теперь я хочу показать, что любой фильтр может быть удовлетворен размышлениями об интерфейсе WCF. API WCF должен быть доступен не для клиентов .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, код действительно не вонял. Теперь это так. Как мне это исправить?

Ответы [ 2 ]

2 голосов
/ 02 декабря 2010

Если вы хотите быть сверхъестественным, вы можете использовать класс System.Linq.Expressions.Expression для динамического построения предиката на основе переданного фильтра. Вы знаете тип, который вы собираетесь искать, поэтому все, что вам нужно сделать, это создать выражение свойства с использованием Filter.Property, а затем константное выражение с Filter.Value. Используйте их, чтобы составить равное выражение, и вы приблизитесь к финишной черте.

Привыкание к составлению выражений может быть трудным делом, но отладчик действительно полезен и покажет вам код для вашего выражения при его составлении, так что погрузитесь и попробуйте!

1 голос
/ 02 декабря 2010

Абсолютно нет способа , вы можете получить свой Func<User, bool> по проводам. Это выражение хранится и будет объединено и будет жить в мире на стороне клиента.

Помните, что вы в основном делаете, чтобы скомпилировать анонимную функцию на стороне клиента с помощью u => u.Roles.Contains(plebRole),Таким образом, вы получите несколько пользователей, которые потом будете фильтровать на стороне клиента.

...