2010-12-02 6 views
3

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

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

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

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

Теперь я хочу, чтобы выставить этот any-filter-can-be-satisfied-мышление над интерфейсом WCF. WCF api должен быть доступен для клиентов non .Net, поэтому я хочу использовать (относительно) простые типы.

У меня есть объект фильтра, который содержит имя свойства и и значение:

[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

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

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

+0

Конечно, вам нужно будет рассмотреть безопасность, поскольку вы не можете позволить конечным пользователям запрашивать какое-либо свойство ваших классов. Возможно, свойства атрибута с настраиваемым атрибутом «ОК для фильтрации», который вы проверяете перед созданием фильтра. – 2010-12-02 14:14:34

+0

Прохладный, это маршрут, который я хочу исследовать. Я не забочусь о безопасности, потому что у меня есть токен аутентификации в объекте запроса, и у меня есть механизм для решения, обрабатывать ли запрос. – grenade 2010-12-02 14:20:47

1

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

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