Я хочу вызвать 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, ...);
и разобьем делегата на задней стороне, чтобы с нетерпением загрузить указанное свойство (ы).
Что я должен использовать для этого?
Ваш 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.
Кажется, что эти параметры не должны беспокоить этот метод, поскольку вы, вероятно, хотите, чтобы все ваше репо загружало свойства лениво или нетерпеливо. Предполагаю, что у вас есть другие методы в этом интерфейсе, помимо 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);
Это аккуратно, потому что вам не нужно позволять вашему коду вызывающего абонента решать, что загружать с нетерпением, и, скорее всего, уменьшает количество сантехнического кода, написанного повсюду. Таким образом, вы создаете нетерпеливое репо и передаете его на кучу ничего не подозревающего кода.
Я придумал, что вам нужно, чтобы все ваши свойства были одного типа (представлены с использованием общего параметра 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.