2016-11-01 9 views
0

Я пытаюсь реализовать фильтр общих элементов, используя Entity Framework, на основе выражения лямбда, указывающего мне поле id. Следующий код компилируется, но, конечно, не работает, так как EF не понимает общую функцию:Фильтрация IQueryable на основе динамического поля

public static IQueryable<T> Authorize<T>(this IQueryable<T> items, Func<T, Guid> idGetter) where T : class 
{ 
    return items.Where(i => idGetter(i) == new Guid("4A6FE5AF-AB63-4BB3-9D32-88766CF242CC")); 
} 

var result = context.Items.Authorize(i => i.Id); 

Как сделать это с помощью EF? Как использовать дерево выражений, чтобы рассказать ему, какое поле сравнивать? Как использовать общее имя в запросе, который может обрабатываться Entity Framework?

ответ

0

Если поле идентификатора всегда типа Guid (как ваш код предполагает), что классы могли бы реализовать интерфейс с вызовом члена Id типа Guid:

public interface MyInterface 
{ 
    Guid Id { get; set; } 
} 

public class Item : MyInterface 
{ 
    public Guid Id { get; set; } 
    // ... 
} 

Тогда ваш метод Авторизоваться может выглядеть это:

public static IQueryable<T> Authorize<T>(this IQueryable<T> items) where T : MyInterface 
{ 
    return items.Where(i => i.Id == new Guid("4A6FE5AF-AB63-4BB3-9D32-88766CF242CC")); 
} 

И вы можете использовать это следующим образом:

var result = items.AsQueryable().Authorize(); 
+0

Благодарим за предложение - на самом деле я уже использую эту реализацию как перегрузку. Но я хочу иметь возможность использовать поля, отличные от указанных в интерфейсе. – user1039407

+0

Возможно, размышление может вам помочь. «Украсьте» свои поля id одним атрибутом, а затем используйте отражение в методе 'Authorize', чтобы получить их. –

0

на основе ваших комментариев и с помощью отражения это мой подход:

Во-первых, ваш (простой) Attribute украсить «поля ID»:

[AttributeUsage(AttributeTargets.Property)] 
public class IdFieldAttribute : Attribute 
{ 
    public IdFieldAttribute() 
    { 
    } 
} 

Тогда ваши классы может выглядеть следующим образом:

public class Item 
{ 
    [IdFieldAttribute] 
    public Guid YourIdField{ get; set; } 
    // ... 
} 

Reflection вспомогательный класс для получения нужного поля:

public static class ReflectionHelper 
{ 
    public static PropertyInfo GetPropertyInfoByAttribute<T>(T o, Type attributeType) where T : class 
    { 
     var type = o.GetType(); 
     var propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) 
      .FirstOrDefault(pi => Attribute.IsDefined(pi, attributeType)); 

     return propertyInfo; 
    } 
} 

После того как вы это, ваш метод Authorize может выглядеть следующим образом:

public static IQueryable<T> Authorize<T>(this IQueryable<T> items) where T : class 
{ 
    return items.Where(i => (Guid)ReflectionHelper.GetPropertyInfoByAttribute(i, typeof(IdFieldAttribute)).GetValue(i) == new Guid("4A6FE5AF-AB63-4BB3-9D32-88766CF242CC")); 
} 

И вы можете использовать его следующим образом:

var result = items.AsQueryable().Authorize(); 

Я не знаю, если то, что вы ищете что-то вроде этого. Возможно, этот подход может помочь вам.

+0

Подход, спасибо. Угадайте, что это более удобно, чем прохождение лямбды каждый раз. Но в любом случае я не думаю, что мне нужно проверить его ... Entity Framework может определенно не обрабатывать этот запрос, не так ли? Итак, у меня была бы такая же проблема сейчас На самом деле возникает вопрос: как использовать общее имя в запросе, который может обрабатываться Entity Framework? – user1039407

+0

Возможно, вам нужно оформить свой запрос ('.ToList()') перед вызовом метода 'Authorize' –

+0

Уверен, что это будет _possible_, но очень медленно для многих объектов. – user1039407

Смежные вопросы