Entity Framework 4/Linq: Как преобразовать из DateTime в строку в запросе?
У меня есть следующий запрос:
from a in Products
select new ProductVM
{
id = a.id,
modified = a.modified.ToString()
}
Что дает мне ошибку:
LINQ to Entities does not recognize the method 'System.String ToString()'
method, and this method cannot be translated into a store expression.
В таблице Products modified
указано значение DateTime.
modified
в классе ProductVM - строка.
Любые идеи? Это должно быть тривиальной проблемой.
Ответы
Ответ 1
ToString()
не поддерживается в Linq to Entities - список вспомогательных функций как часть SqlFunctions, но это не означает, t поддерживает преобразование даты в строку.
Простейшим было бы сначала создать анонимный тип в запросе, а затем применить к IEnumerable
с помощью AsEnumerable()
- после этого вы можете использовать ToString()
, потому что теперь вы используете Linq для объектов для остальной части выражения запроса (там длинная статья на этом здесь).
var results = Products.Select( p => new { a.id, a.modified })
.AsEnumerable()
.Select(p => new ProductVM()
{ id = p.id,
modified = p.modified.ToString()
});
Ответ 2
Здесь альтернатива:
.Select( p -> SqlFunctions.StringConvert((double)
SqlFunctions.DatePart("m", p.modified)).Trim() + "/" +
// SqlFunctions.DateName("mm", p.modified) + "/" + MS ERROR?
SqlFunctions.DateName("dd", p.modified) + "/" +
SqlFunctions.DateName("yyyy", p.modified)
По-видимому, DateName("MM", ..)
указывает название месяца, в котором DatePart("mm", ..)
содержит числовое значение, таким образом, StringConvert( )
, но это оставляет пропуски результата с пробелами, таким образом, .Trim()
.
Ответ 3
Это может не добавить много, но на всякий случай кто-то такой же сумасшедший, как и я, вот полный код, если вам нужно построить дерево выражений для ответа доктора Зима, используя DatePart/DateName, включая также часть времени. Очевидно, что для других целей вы можете изменить Product- > YourInitialType, ProductVM- > YourResultType и modified- > YourProperty.
Изменить (1/23/08): SQL, сгенерированный из этого, изменился между 6.0.2 и 6.1.3. Первоначально, если значение было null, генерируемый SQL создавал бы нулевой результат. Я считал это желательным в этом случае, но я могу понять, почему в других случаях это было бы нежелательно (нуль + "строковое значение" = нуль) и может привести к тому, что результат не будет соответствовать тому, что вы бы предпочли. Я подробно расскажу о том, как вывод столбца изменился ниже, но его трение заключается в том, что теперь он будет выводить "//::" для нулевых значений. Я просто обработал этот вывод в своем кодовом коде как особый случай и вручную изменил его на null, но другие могут захотеть найти более надежные результаты, чтобы обеспечить вывод нулей в качестве значения null. Также стоит отметить, что SQL-запрос очень длинный в новой версии.
ParameterExpression paramExp = Expression.Parameter(typeof(Product));
string propertyName = "modified";
Expression propertyOrField = Expression.PropertyOrField(paramExp, propertyName);
MethodInfo datePartMethod = typeof(System.Data.Entity.SqlServer.SqlFunctions).GetMethods().Where(x => x.Name == "DatePart" && x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType == typeof(DateTime?)).First();
MethodInfo dateNameMethod = typeof(System.Data.Entity.SqlServer.SqlFunctions).GetMethods().Where(x => x.Name == "DateName" && x.GetParameters().Length == 2 && x.GetParameters()[1].ParameterType == typeof(DateTime?)).First();
MethodInfo stringConvertMethod = typeof(System.Data.Entity.SqlServer.SqlFunctions).GetMethods().Where(x => x.Name == "StringConvert" && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(decimal?)).First();
MethodInfo stringConcatMethod = typeof(string).GetMethods().Where(x => x.Name == "Concat" && x.GetParameters().Length == 2 && x.GetParameters()[0].ParameterType == typeof(string) && x.GetParameters()[1].ParameterType == typeof(string)).First();
MethodInfo stringTrimMethod = typeof(string).GetMethods().Where(x => x.Name == "Trim" && x.GetParameters().Length == 0).First();
Type projectedType = typeof(ProductVM);
NewExpression newHolder = Expression.New(projectedType);
MemberInfo member = anonType.GetMember("modified")[0];
var monthPartExpression = Expression.Call(null, datePartMethod, Expression.Constant("mm", typeof(string)), propertyOrField);
var convertedMonthPartExpression = Expression.Call(null, stringConvertMethod, Expression.Convert(monthPartExpression, typeof(decimal?)));
var convertedDayPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("dd", typeof(string)), propertyOrField);
var convertedYearPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("yyyy", typeof(string)), propertyOrField);
var convertedHourPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("hh", typeof(string)), propertyOrField);
var convertedMinutePartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("n", typeof(string)), propertyOrField);
var convertedSecondPartExpression = Expression.Call(null, dateNameMethod, Expression.Constant("ss", typeof(string)), propertyOrField);
var allAddedExpression = Expression.Call(null, stringConcatMethod,
convertedMonthPartExpression,
Expression.Call(null, stringConcatMethod,
Expression.Constant("/", typeof(string)),
Expression.Call(null, stringConcatMethod,
convertedDayPartExpression,
Expression.Call(null, stringConcatMethod,
Expression.Constant("/", typeof(string)),
Expression.Call(null, stringConcatMethod,
convertedYearPartExpression,
Expression.Call(null, stringConcatMethod,
Expression.Constant(" ", typeof(string)),
Expression.Call(null, stringConcatMethod,
convertedHourPartExpression,
Expression.Call(null, stringConcatMethod,
Expression.Constant(":", typeof(string)),
Expression.Call(null, stringConcatMethod,
convertedMinutePartExpression,
Expression.Call(null, stringConcatMethod,
Expression.Constant(":", typeof(string)),
convertedSecondPartExpression
))))))))));
var trimmedExpression = Expression.Call(allAddedExpression, stringTrimMethod, new Expression[] { });
var month = Expression.Bind(member, trimmedExpression);
MemberInitExpression memberInitExpression =
Expression.MemberInit(
newHolder,
new MemberBinding[] { month });
var lambda = Expression.Lambda<Func<Product, ProductVM>>(memberInitExpression, paramExp);
Ответ 4
Создайте новый POCO с этой структурой (я предполагаю, что тип данных - DateTime):
public class UserProductVM {
...
private DateTime _modified;
public DateTime SetModified { set { _dateEvent = value; } }
public string Modified { get { return _modified.ToString("dd MMM yyyy @ HH:mm:ss"); } }
...
}
Затем вы назначаете значение SetModified, меняя свой код следующим образом:
from a in Products
select new UserProductVM
{
...
SetModified = a.modified
}
Обратите внимание, что я использую UserProductVM вместо ProductVM и SetModified вместо изменен.
Затем, когда вы получите свойство Модифицированное, новый POCO отобразит его как строку, которую вы отформатировали.