2011-03-20 20 views
17

Tengo la siguiente consulta:Entity Framework 4/Linq: ¿Cómo convertir de DateTime a cadena en una consulta?

from a in Products 
select new ProductVM 
    { 
     id = a.id, 
     modified = a.modified.ToString() 
    } 

Lo que me da un error de:

LINQ to Entities does not recognize the method 'System.String ToString()' 
method, and this method cannot be translated into a store expression. 

El modified en los productos tabla es DateTime. El modified en la clase ProductVM es una cadena.

¿Alguna idea? Esto tiene que ser un tema trivial.

Respuesta

19

ToString() no está soportada en LINQ a Entidades - hay una lista de los ayudantes de función como parte de SqlFunctions pero esto no es compatible con la fecha de conversión de cadenas.

más fácil sería el primer proyecto a un tipo anónimo dentro de la consulta y luego se convierte en una IEnumerable mediante el uso de AsEnumerable() - después de que se puede utilizar ToString() porque ahora está usando LINQ a Objetos para el resto de la expresión de consulta (hay un extenso artículo sobre este tema here).

var results = Products.Select(p => new { a.id, a.modified }) 
         .AsEnumerable() 
         .Select(p => new ProductVM() 
           { id = p.id, 
            modified = p.modified.ToString() 
           }); 
+0

Fantástico. Acabo de usar mis POCO antes, luego, en el último minuto, utilicé .AsEnumerable(). Select (p ... y funcionó muy bien. –

+1

Parece que el enlace "Artículo extenso en este tema" está roto. :-( –

+0

@BrokednGlass Buena respuesta. Gracias – User

34

Aquí hay una alternativa:

.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) 

Al parecer DateName("MM", ..) se detalla el nombre del mes en que DatePart("mm", ..) proporciona un valor numérico, por lo tanto el StringConvert(), pero esta almohadillas izquierda el resultado con espacios, por lo tanto, la .Trim().

+1

¡Esto funciona genial! Necesitaba hacer esto para mantener mi objeto original como IQueryable. ¡Gracias! – duyn9uyen

+0

Simple y sin conversión a primer enumerable – brian

+0

Esta es una respuesta realmente útil. Gracias :-) –

0

Esto puede no agregar mucho, pero por si alguien está tan loco como yo, aquí está el código completo si necesita construir el árbol de expresiones para la respuesta del Dr. Zim utilizando DatePart/DateName incluyendo la parte de tiempo también . Obviamente, para otros fines, puede cambiar Product-> YourInitialType, ProductVM-> YourResultType y modified-> YourProperty.

Editar (1/23/08): El SQL generado a partir de esto cambió entre 6.0.2 y 6.1.3. Inicialmente, si el valor fuera nulo, el SQL generado crearía un resultado nulo. Lo consideré conveniente en este caso, pero puedo ver por qué en otros casos no sería deseable (nulo + "un valor de cadena" = nulo) y podría causar un resultado no igual al que preferiría. Detallaré cómo la salida de la columna cambió a continuación, pero la frustración es que ahora obtendrá "// ::" para valores nulos. Simplemente manejé esta salida en mi código de llamada como un caso especial y lo cambié manualmente a nulo, pero otros pueden querer abordar la adición de resultados más sólidos para garantizar que los resultados nulos sean nulos. También vale la pena señalar que la instrucción SQL es muy larga en la nueva versión.

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); 
0

Crear un nuevo POCO con esta estructura (estoy suponiendo que el tipo de datos es 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"); } } 
    ... 
} 

A continuación, se asigna el valor a SetModified, cambiar su código como este:

from a in Products 
select new UserProductVM 
{ 
    ... 
    SetModified = a.modified 
} 

Tener en cuenta que estoy usando UserProductVM lugar ProductVM y SetModified en su lugar modificado.

Entonces, cuando llegue la propiedad Modified, el nuevo POCO va a traer como la cadena que ha formateado.

Cuestiones relacionadas