Я использую Entity Framework 4.1 Code First. В моей сущности у меня есть три свойства даты и времени:
public class MyEntity
{
[Key]
public Id { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
[NotMapped]
public DateTime? QueryDate { get; set; }
// and some other fields, of course
}
В базе данных всегда указываются даты «От» и «До». Я запрашиваю их, используя простое предложение where. Но в результирующий набор я хочу включить дату, которую я запрашивал. Мне нужно сохранить это, чтобы работала другая бизнес-логика.
Я работаю над методом расширения, чтобы сделать это, но у меня возникают проблемы:
public static IQueryable<T> WhereDateInRange<T>(this IQueryable<T> queryable, DateTime queryDate) where T : MyEntity
{
// this part works fine
var newQueryable = queryable.Where(e => e.FromDate <= queryDate &&
e.ToDate >= queryDate);
// in theory, this is what I want to do
newQueryable = newQueryable.Select(e =>
{
e.QueryDate = queryDate;
return e;
});
return newQueryable;
}
Это не работает. Он работает, если я использую IEnumerable, но я хочу сохранить его как IQueryable, чтобы все работало на стороне базы данных, и этот метод расширения все еще можно использовать в любой части другого запроса. Когда это IQueryable, я получаю следующую ошибку компиляции:
Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражения
Если бы это был SQL, я бы сделал что-то вроде этого:
SELECT *, @QueryDate as QueryDate
FROM MyEntities
WHERE @QueryDate BETWEEN FromDate AND ToDate
Итак, вопрос в том, как я могу преобразовать дерево выражения, которое я уже нужно включить это дополнительное присвоение собственности? Я просмотрел IQueryable.Expression и IQueryable.Provider.CreateQuery - где-то там есть решение. Может быть, к существующему дереву выражений можно добавить выражение присваивания? Я недостаточно знаком с методами дерева выражений, чтобы понять это. Есть идеи?
Пример использования
Чтобы уточнить, цель состоит в том, чтобы иметь возможность выполнить что-то вроде этого:
var entity = dataContext.Set<MyEntity>()
.WhereDateInRange(DateTime.Now)
.FirstOrDefault();
И сохранить DateTime.Now в QueryDate результирующей строки, БЕЗ наличия более одной строки возвращается из запроса к базе данных. (В решении IEnumerable несколько строк возвращаются до того, как FirstOrDefault выберет нужную строку.)
Другая идея
Я мог бы пойти дальше и сопоставить QueryDate как реальное поле и установить для его DatabaseGeneratedOption значение Computed. Но тогда мне понадобится какой-то способ внедрить «@QueryDate как QueryDate» в SQL, созданный операторами select EF. Поскольку он вычисляется, EF не будет пытаться предоставить значения во время обновления или вставки. Итак, как я могу внедрить пользовательский SQL в операторы select?