В моем коде C# у меня есть 2 запроса WHERE, оба из которых я могу вызвать IQueryable, и все это скомпилировано до SQL, и у обоих из них есть много общей логики ,Использование функции C# в Entity Framework Query
Я считаю, что это не дублирование этого похожего вопроса: Using Function in Select Clause of Entity Framework Query, потому что в моем сценарии функция, о которой идет речь, может быть преобразована в SQL-EF, просто не понимая, что она может это сделать.
Запросы примерно:
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<Template> set, User user)
{
return set.Where(temp =>
temp.Requests
.Where(req => req.WasSent)
.OrderByDescending(req => req.DueDate)
.Take(2)
.SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id))
.Contains(user.Id));
}
И
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<DataReturn> set, User user)
{
return set.Where(ret=>
ret.Entity.Id == user.Entity.Id
&&
ret.Request.Template.Requests
.Where(req => req.WasSent)
.OrderByDescending(req => req.DueDate)
.Take(2)
.SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id))
.Contains(user.Id));
}
Так основное правило BusinessLogic для «владеет шаблон», а затем следствие того, что для «владеет DataReturn если спичек компании и владеет шаблон "
Как вы можете видеть, думая только о C#, они могут быть легко реорганизованы как:
private static bool UserOwnsTemplate(User user, Template temp)
{
return temp.Requests
.Where(req => req.WasSent)
.OrderByDescending(req => req.DueDate)
.Take(2)
.SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id))
.Contains(user.Id);
}
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<Template> set, User user)
{
return set.Where(temp => UserOwnsTemplate(user, temp));
}
public static IQueryable<DataReturn> WhereIsOwnedByUser(this IQueryable<DataReturn> set, User user)
{
return set.Where(
ret =>
ret.Entity.Id == user.Entity.Id
&&
UserOwnsTemplate(user, ret.Request.Template)
);
}
Таким образом, сокращение дублирования (Yay!)
Но то EF будет жаловаться, что он не знает, что делать с UserOwnsTemplate
, несмотря на то, что он может справиться с логикой в SQL отлично.
AFAICT нет прекрасного способа решить эту проблему. Я думаю мои варианты:
- Включите
UserOwnsTemplate
в UDF, функцию SQL, определенную в базе данных.- Но я не могу создать UDF из C# lamda, я должен определить SQL, который будет более сложным.
- Присвоить
Expression<Func<Template,bool>>
, чтоUserOwnsTemplate
определяет в качестве переменной, а затем построить соответствующуюExpression<Func<DataReturn ,bool>>
для версии DataReturn вручную, используяExpression.AndAlso
склеить два «положения» вместе.- Мета-программирование. Ughhh. Я сделал это раньше в другом проекте, и это было противно, и кошмар для поддержания.
- Live с дублированием.
- Вероятно, что произойдет, если ТО не сообщит иначе. ;)
Можно ли увидеть какие-либо другие варианты, доступные?
Могу ли я что-либо сделать, чтобы заставить EF анализировать функцию в SQL? (на ум приходит фраза «inling», но я не знаю, что я думаю об этом?)
Может ли кто-нибудь увидеть способ преобразования ret.Request.Template в IQueryable, чтобы я мог просто назовите другой метод расширения WhereIsOwnedBy?
Любые другие предложения НА ВСЕ?
Что касается вашего 2-ого пункта пули, я думаю, что немного AndAlso не требует каких-либо пререкание дерева выражений. Вы всегда можете выполнить 'set.Where (выражение) .Where (anotherExpression)'. Трудность возникает из-за того, что ваше выражение зависит от пользователя и что вы хотите применить его к другому пути от корня IQueryable (temp vs. ret.Request.Template). Я не вижу, как обращаться с ними, не создавая собственного выражения. Возможно, есть и другой способ, но я ожидаю, что это приведет к существенному изменению запросов, которые вы делаете. – hgcummings
Дубликат http://stackoverflow.com/questions/7066305/how-to-stay-dry-whilst-using-linq-to-entities-and-helper-methods? – Dan