С# лямбда-запрос с использованием общего типа
У меня есть три класса, все они имеют свойство Date. Я хотел бы написать общий класс для возврата всех записей за одну дату.
Теперь проблема заключается в следующем: как я могу написать выражение лямбда, используя общий тип T?
Простой код такой, как показано ниже (я не буду компилировать, потому что "r.Date" не будет работать, но это эффект, который я хотел бы достичь)
Class GenericService<T>: IGenericService<T> where T:class
{
...
readonly IGenericRepository<T> _genericRepository;
public IEnumerable<T> GetRecordList(DateTime date)
{
var query=_genericRepository.FindBy(r=>r.Date=date);
}
Спасибо за помощь!
С уважением,
Leona
Ответы
Ответ 1
Другим вариантом является динамическое создание Expression
. Это может быть полезно, если вы не хотите или не можете добавить интерфейс IDate
к объектам модели, а если FindBy
нужен Expression<Func<T, bool>>
(например, он переводит на IQueryable<T>.Where(...)
внутренне для EntityFramework или аналогично.
Как @JonB ответ, у этого нет безопасности во время компиляции, чтобы гарантировать, что T
имеет свойство или поле Date
. Он будет работать только во время выполнения.
public IEnumerable<T> GetRecordList(DateTime date)
{
var parameterExpression = Expression.Parameter(typeof(T), "object");
var propertyOrFieldExpression = Expression.PropertyOrField(parameterExpression, "Date");
var equalityExpression = Expression.Equal(propertyOrFieldExpression, Expression.Constant(date, typeof(DateTime)));
var lambdaExpression = Expression.Lambda<Func<T, bool>>(equalityExpression, parameterExpression);
var query = _genericRepository.FindBy(lambdaExpression);
return query;
}
Ответ 2
Напишите интерфейс, который имеет IDate
, и все ваши сущности должны реализовать IDate
и написать GenericService
следующим образом:
public class GenericService<T>: IGenericService<T> where T : class, IDate
{
readonly IGenericRepository<T> _genericRepository;
public IEnumerable<T> GetRecordList(DateTime date)
{
var query=_genericRepository.FindBy(r => r.Date = date);
}
}
public interface IDate
{
DateTime Date{ set; get; }
}
public class Entity : IDate
{
DateTime Date { set; get; }
}
Ответ 3
Другое решение, если вы не можете реализовать интерфейс и не хотите использовать отражение, может состоять в том, чтобы передать, как получить дату в GenericService:
По свойству:
public class GenericService<T>: IGenericService<T> where T : class
{
public Func<T, DateTime> DateGetter { get; set; }
readonly IGenericRepository<T> _genericRepository;
public IEnumerable<T> GetRecordList(DateTime date)
{
var query=_genericRepository.FindBy(r => DateGetter(r) == date);
}
}
По конструктору:
public class GenericService<T>: IGenericService<T> where T : class
{
Func<T, DateTime> _dateGetter;
public GenericService(..., Func<T, DateTime> dateGetter)
{
_dateGetter = dateGetter
...
}
readonly IGenericRepository<T> _genericRepository;
public IEnumerable<T> GetRecordList(DateTime date)
{
var query=_genericRepository.FindBy(r => _dateGetter(r) == date);
}
}
С помощью самого метода:
public class GenericService<T>: IGenericService<T> where T : class
{
readonly IGenericRepository<T> _genericRepository;
public IEnumerable<T> GetRecordList(DateTime date, Func<T, DateTime> dateGetter)
{
var query=_genericRepository.FindBy(r => dateGetter(r) == date);
}
}
По типу:
Решение Адама Ральфа вдохновило меня на это (не лучшее, что я думаю, но оно все еще работает)
public class GenericService<T>: IGenericService<T> where T : class
{
public Dictionary<Type, Func<object, DateTime>> _dateGetter = new Dictionary<Type, Func<object, DateTime>>()
{
{ typeof(TypeWithDate), x => ((TypeWithDate)x).Date },
{ typeof(TypeWithDate2), x => ((TypeWithDate2)x).Date }
};
readonly IGenericRepository<T> _genericRepository;
public IEnumerable<T> GetRecordList(DateTime date)
{
var query=_genericRepository.FindBy(r => _dateGetter[typeof(T)](r) = date);
}
}
Ответ 4
Предполагая, что список объектов не огромен, запись чего-то общего для 3 классов может быть чрезмерной. KISS.
objects.Where(obj =>
(obj as Foo)?.DateTime == date ||
(obj as Bar)?.DateTime == date ||
(obj as Baz)?.DateTime == date)
Ответ 5
Я не уверен, как ваш метод FindBy
структурирован или что он возвращает, но вы можете использовать ключевое слово dynamic
следующим образом:
var query=_genericRepository.FindBy(r=>((dynamic)r).Date==date);
Если метод FindBy
возвращает IEnumerable<T>
, вам может потребоваться добавить .Cast<T>()
в оператор return, иначе вы можете получить тип IEnumerable<dynamic>
.
Это ни в коем случае не является типичным, и вы можете получить ошибки времени выполнения, если T
не имеет свойства Date
. Но это один из вариантов для рассмотрения.