Подтвердить что ты не робот

С# лямбда-запрос с использованием общего типа

У меня есть три класса, все они имеют свойство 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

4b9b3361

Ответ 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. Но это один из вариантов для рассмотрения.