Linq - повторно использовать выражение для дочернего свойства - PullRequest
1 голос
/ 15 марта 2010

Не уверен, возможно ли то, что я пытаюсь сделать, или нет, но я хотел бы повторно использовать выражение linq для родительского свойства объектов.

С учетом классов:

class Parent {
 int Id { get; set; }
 IList<Child> Children { get; set; }
 string Name { get; set; }
}
class Child{
 int Id { get; set; }
 Parent Dad { get; set; }
 string Name { get; set; }
}

Если у меня есть помощник

Expression<Func<Parent,bool> ParentQuery() { 
  Expression<Func<Parent,bool> q = p => p.Name=="foo";
}

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

using(var context=new Entities.Context) {
 var data=context.Child.Where(c => c.Name=="bar" 
 && c.Dad.Where(ParentQuery));
}

Я знаю, что могу сделать это для дочерних коллекций:

using(var context=new Entities.Context) {
 var data=context.Parent.Where(p => p.Name=="foo" 
 && p.Childen.Where(childQuery));
}

но не вижу способа сделать это для свойства, которое не является коллекцией.
Это просто упрощенный пример, на самом деле ParentQuery будет более сложным, и я хочу избежать повторения этого в нескольких местах, так как вместо двух слоев у меня будет ближе к 5 или 6, но все они должны будут ссылаться родительский запрос для обеспечения безопасности.

Если это невозможно, моей другой мыслью было как-то так эффективно перевести выражение ParentQuery для данного типа: p => p.Name == "foo"; превращается в: c => c.Dad.Name == "foo"; но используя дженерики / некоторую другую форму построителя запросов, которая позволяет сохранить родительский запрос, а затем просто создать транслятор для дочернего объекта, который заменяет в свойстве маршрут к родителю.

EDIT: Исходя из комментариев @David Morton

Первоначально похоже, что я могу просто перейти от Expression к функции делегата, а затем вызвать .Где (ParentQuery () (c.Dad));

Однако я использую это в более широком шаблоне репозитория и не могу понять, как я могу использовать это с универсальными шаблонами и построителями предикатов - я не хочу получать строки из хранилища и фильтровать на клиенте (в данном случае веб-сервер). У меня есть общий метод получения данных, который принимает запрос базового выражения. Затем я хочу проверить, реализует ли предоставленный тип ISecuredEntity и добавляет ли он securityQuery для сущности, с которой мы имеем дело.

public static IList<T> GetData<T >(Expression<Func<T, bool>> query) {
 IList<T> data=null;
 var secQuery=RepositoryHelperers.GetScurityQuery<T>();
 if(secQuery!=null) {
  query.And(secQuery);
 }
 using(var context=new Entities.Context()) {
  var d=context.GetGenericEntitySet<T>();
  data=d.ToList();
 }
 return data;
}

ISecuredEntity:

public interface ISecuredEntity : IEntityBase {
    Expression<Func<T, bool>> SecurityQuery<T>();
}

Пример сущности:

public partial class ExampleEntity:  ISecuredEntity {
    public Expression<Func<T, bool>> SecurityQuery<T>() {
        //get specific type expression and make generic
        Type genType = typeof(Func<,>).MakeGenericType(typeof(ExampleEntity), typeof(bool));
       var q = this.SecurityQuery(user);
        return (Expression<Func<T, bool>>)Expression.Lambda(genType, q.Body, q.Parameters);         
    }

     public Expression<Func<ExampleEntity, bool>> SecurityQuery() {
        return e => e.OwnerId==currentUser.Id;

    }
}

и репозиторий Helpers:

internal  static partial class RepositoryHelpers {
    internal static Expression<Func<T, bool>> SecureQuery<T>() where T : new() {
        var instanceOfT = new T();
        if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) {
            return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>();
        }
        return null;
    }
}

РЕДАКТИРОВАТЬ Вот (возможное) решение

В итоге я вернулся к использованию выражений и использованию LinqKit Invoke. Примечание: для EF я также должен был вызвать .AsExpandable () для entitySet

Ключевая часть в состоянии звонить:

 Product.SecureFunction(user).Invoke(pd.ParentProduct);

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

Мои конечные классы выглядят так:

public interface ISecureEntity {
 Func<T,bool> SecureFunction<T>(UserAccount user);
}


public class Product : ISecureEntity {
 public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) {
  return SecureFunction(user) as Expression<Func<T,bool>>; 
 }
 public static Expression<Func<Product,bool>> SecureFunction(UserAccount user) {
  return f => f.OwnerId==user.AccountId;
 }
 public string Name { get;set; }
 public string OwnerId { get;set; }
}


public class ProductDetail : ISecureEntity {
 public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) {
  return SecureFunction(user) as Expression<Func<T,bool>>; 
 }
 public static Func<ProductDetail,bool> SecureFunction(UserAccount user) {
  return pd => Product.SecureFunction(user).Invoke(pd.ParentProduct);
 }
 public int DetailId { get;set; }
 public string DetailText { get;set; }
 public Product ParentProduct { get;set; }
}

Использование:

public IList<T> GetData<T>() {
 IList<T> data=null;
 Expression<Func<T,bool>> query=GetSecurityQuery<T>();
 using(var context=new Context()) {
  var d=context.GetGenericEntitySet<T>().Where(query);
  data=d.ToList();
 }
 return data;
}
private Expression<Func<T,bool>> GetSecurityQuery<T>() where T : new() {
  var instanceOfT = new T();
        if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) {
            return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>(GetCurrentUser());
        }
        return a => true; //returning a dummy query
    }
}

Спасибо за помощь всем.

1 Ответ

1 голос
/ 15 марта 2010

Ты слишком обдумываешь это.

Во-первых, не возвращайте Expression<Func<Parent, bool>>, который потребует от вас компиляции выражения. Вместо этого верните просто Func<Parent, bool>.

Далее, все в том, как вы это называете:

 context.Children.Where(c => c.Name == "bar" && ParentQuery()(c.Dad));

 context.Parents.Where(ParentQuery());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...