Выражения Linq как params

1

Я хочу вызвать IQueryable<T> из моего репозитория, но я хочу охотно загружать дочерние объекты в свой ORM. Чтобы сохранить логику сохранения в уровне персистентности, я хочу передать список выражений, представляющих свойства, которые я хочу с нетерпением загрузить.

Мой IRepository<TClass> выглядит следующим образом:

IQueryable<TClass> AsQueryable();

Я хотел бы сделать это так:

IQueryable<TClass> AsQueryable(params --something here--[] includeProperties);

... так что я могу назвать это так:

var q = myFooRepository.AsQueryable(x => x.Property1, x => x.Property2, ...);

и разобьем делегата на задней стороне, чтобы с нетерпением загрузить указанное свойство (ы).

Что я должен использовать для этого?

Теги:
linq
delegates
lambda
expression

3 ответа

1
Лучший ответ

Ваш AsQueryable<TClass> должен иметь выражения свойств как параметры, и для него должна быть следующая подпись:

public static IQueryable<TClass> AsQueryable<TClass>(this TClass obj, params Expression<Func<TClass, object>>[] propertyExpressions)

Обратите внимание, что мы используем Func<TClass, object> который представляет собой функцию, которая захватывает TClass в качестве входных данных и возвращает объект. Это позволяет нам сделать звонок следующим образом:

IQueryable<TClass> tClassQueryable = tClassObj.AsQueryable(x => x.Property1, x => x.Property2);

Также обратите внимание, что я не выбрал object как TResult из Func<TClass, object> случайно. Поскольку общий параметр TResult делегата функции является ковариантным, это позволяет нам передавать выражения даже с разными типами свойств. Таким образом, ваши Property1 и Property2 в приведенном выше примере не должны быть одного типа.

Это было бы так, в отношении вашего вопроса, я думаю, но здесь немного больше:

Если вы случайно должны оценить переданные выражения, чтобы использовать их с ORM (например, вам просто нужны имена свойств, но вы хотите передать их в виде выражений, чтобы избежать имен жесткого диска и сохранять проверки времени компиляции), вам понадобится что-то вроде этого:

  public static IQueryable<TClass> AsQueryable<TClass>(this TClass obj, params Expression<Func<TClass, object>>[] propertyExpressions)
    {
        foreach (var propertyExpression in propertyExpressions)
        {
            MemberExpression memberExpression = propertyExpression.Body as MemberExpression;

            if (memberExpression == null)
            {
                // this is needed for value types properties.
                UnaryExpression unaryExpression = (UnaryExpression)propertyExpression.Body;
                memberExpression = unaryExpression.Operand as MemberExpression;
            }

            if (memberExpression == null)
                throw new ArgumentException(string.Format("Expression '{0}' is not a member expression.", propertyExpression.ToString()));

            PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
            if (propertyInfo == null)
                throw new ArgumentException("MemberExpression.Member is not a PropertyInfo.");


            // at this point we have PropertyInfo which you can use with your OR Mapper to further implement logic which will eager load the property
            // e.g. property name can be retrieved with:
            string propertyName = propertyInfo.Name;

            // do your ORM stuff here
        }
    }

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

1

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

Таким образом, другой подход должен был оставить эту функциональность вне метода AsQueryable(), но вместо этого создать дополнительные методы, которые создавали бы новую оболочку, настроенную на загрузку с нетерпением (я бы не мутировал базовый экземпляр).

Т.е.

// default repo is lazy
var myRepo = GetRepository<TClass>();

// these cool fluent methods create an eager repo
var myEagerRepo = myRepo
   .LoadEagerly(x => x.Property1)
   .LoadEagerly(x => x.Property2);

Это аккуратно, потому что вам не нужно позволять вашему коду вызывающего абонента решать, что загружать с нетерпением, и, скорее всего, уменьшает количество сантехнического кода, написанного повсюду. Таким образом, вы создаете нетерпеливое репо и передаете его на кучу ничего не подозревающего кода.

  • 0
    Это хорошее предложение для адаптации дизайна, но я думаю, что оно не отвечает на этот вопрос, который на самом деле: как получить выражения свойств linq в качестве параметров?
  • 0
    @ Edin: верно, вы ответили на это, и я даже не упомянул, как решить эту проблему (я даже специально использовал один метод для каждого свойства). ИМХО, я все еще верю, что этот ответ может быть полезным, и он не совсем подходит для комментария.
Показать ещё 2 комментария
0

Я придумал, что вам нужно, чтобы все ваши свойства были одного типа (представлены с использованием общего параметра O в следующем примере кода):

class SampleClass
{
    public string Property { get; set; }
    public SampleClass()
    {
        Property = DateTime.Now.ToString();
    }
}

class Program
{
    public static O Something<I, O>(params System.Linq.Expressions.Expression<Func<I, O>>[] funks)
        where I: new()
    {
        I i = new I();
        var output = funks[0].Compile()(i);
        return output;
    }

    static void Main(string[] args)
    {
        var dx= Something<SampleClass, string>(x => x.Property);
    }
}

надеюсь, это поможет. Я все еще думаю, как я могу быть другим. хороший вопрос! на самом деле вам не нужно использовать System.Linq.Expression.Expression>, а просто Func.

  • 0
    ну нет, типы собственности будут другими. Это сервисный уровень для ORM; каждое свойство, которое я бы хотел загрузить, было бы другого типа.
  • 0
    Затем вы можете заменить Func <I, O> на Func <I, object> (где I - ваш тип сущности).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню