2012-02-14 11 views
5

He estado golpeando mi cabeza con este problema por algún tiempo. Hay algunos casos similares, pero las soluciones no eran aplicables en mi caso.Dynamic LINQ (a entidades) Donde con la columna de fecha y hora anulable

Tengo un método que devuelve consulta de filtro en formato de cadena. El método tiene la lógica para diferentes tipos de datos, establece valores correctos, los nombres de columnas etc.

string filterQuery = GetFilterQuery(params); 
rows = rows.Where(filterQuery); 

Mi problema es que tengo Nullable DateTime en la base de datos y tengo String representación en el lado del código.

he tratado siguientes consultas (String representación podrían estar equivocados actualmente):

"BirthDate.ToString() = \"16.2.2012 22:00:00\"" 

Resultado: '? DateTime' Métodos del tipo de no son accesibles

"BirthDate.Value.ToString() = \"16.2.2012 22:00:00\"" 

Resultado: LINQ a Entidades no reconoce el método de método 'System.String ToString()', y este método no se puede traducir en una expresión tienda.

"BirthDate == null ? 1=1 : (DateTime)BirthDate.ToString() = \"16.2.2012 22:00:00\"" 

Resultado: ''. o '(' espera

Alguna idea de cómo resolver el problema?

actualización (más código fuente añadió sobre la generación de consultas)

var filterQueries = query.GridFilteringOptions.filters 
    // remove filters that doesn't have all the required information 
    .Where(o => o.name != string.Empty && o.value != string.Empty && !string.IsNullOrEmpty(o.type)) 
    // remove filters that are filtering other tables than current 
    .Where(o => o.table == tableName) 
    .Select(filter => filter.ResolveQuery()).ToList(); 

if (filterQuery.Any()) 
{ 
    var filterQuery = string.Join(" And ", filterQueries); 
    rows = rows.Where(filterQuery); 
} 

Y aquí es un filtro de clase y métodos se en relación con este contexto

public string ResolveQuery() 
{ 
    if (type == "Int64") 
    { 
     return ResolveInteger(); 
    } 
    else if(type == "String") 
    { 
     return ResolveString(); 
    } 
    else if(type == "DateTime") 
    { 
     return ResolveDateTime(); 
    } 
    else 
    { 
     return string.Empty; 
    } 
} 

private string ResolveDateTime() 
{ 
    DateTime result = new DateTime(); 
    if (DateTime.TryParse(this.value, out result)) 
    { 
     return string.Format("{0}.ToString() = \"{1}\"", this.name, result.ToUniversalTime()); 
    } 
    return string.Empty; 
} 

private string ResolveString() 
{ 
    switch (@operator) 
    { 
     default: 
      return string.Format(@"{0}.StartsWith(""{1}"")", this.name, this.value); 
    }    
} 

private string ResolveInteger() 
{ 
    string tmp = this.name; 
    switch (@operator) 
    { 
     case -1: 
      return string.Empty; 
     case 0: 
      tmp += "<"; 
      break; 
     case 1: 
      tmp += "="; 
      break; 
     case 2: 
      tmp += ">"; 
      break; 
     default: 
      return string.Empty; 
    } 
    tmp += value; 
    return tmp; 
} 

Respuesta

2

LINQ a Entidades no reconoce laMétodoDebería evaluarlo antes de insertar la cadena resultante en su consulta.

Para crear los ejemplos en su pregunta que pueda manejarlo como esto:

// "BirthDate.ToString() = \"16.2.2012 22:00:00\"" 
string birthdate = BirthDate.ToString(); 
string query = String.Format("{0} = \"16.2.2012 22:00:00\"", birthdate); 

// "BirthDate.Value.ToString() = \"16.2.2012 22:00:00\"" 
string birthdate = BirthDate.Value.ToString(); 
string query = String.Format("{0} = \"16.2.2012 22:00:00\"", birthdate); 

"BirthDate == null ? 1=1 : (DateTime)BirthDate.ToString() = \"16.2.2012 22:00:00\"" probablemente no funciona debido a LINQ EF no reconoce el operador ternario (? :)

Editar: Entiendo por su comentario que BirthDate es una columna en su tabla, no una variable.En este caso se puede recuperar todas las entradas, convertirlos en una lista y luego aplicar el filtro utilizando LINQ a objetos como esto (aunque habría que modificar filterQuery en consecuencia):

string filterQuery = GetFilterQuery(params); 
var filteredRows = rows.ToList().Where(filterQuery); 

No comprobado: Se podría sea ​​posible utilizar la función de su base de datos CONVERT:

string query = "CONVERT(varchar(20), BirthDate) = \"16.2.2012 22:00:00\""; 
+0

BirthDate.ToString() - ¿Qué es BirthDate en este contexto? En mi caso, es solo un nombre de columna, por lo que no existe en el contexto del código. – Tx3

+0

Ya veo. Eche un vistazo a mi respuesta actualizada. –

+0

Gracias, probaré el enfoque no probado también – Tx3

0

Mi problema es que tengo que aceptan valores NULL Fecha y hora en la base de datos y no tengo representación de cadena en el lado código.

¿Por qué?

Cambie el lado del código, analice la representación de cadena y asígnele el objeto adecuado en la consulta LINQ.

"BirthDate.ToString() = \" 16.2.2012 22: 00: 00 \ ""

BAD - Si tiene que hacer manipulatzion cadena, AWLWAYS utilizar InvariantCulture. Este código es frágil y se romperá en cualquier otro país.

+0

El formato de la fecha no es relevante en esta pregunta. Sí, no está formateado correctamente en el código de ejemplo. Podría cambiarse a entero UTC. Lo que intento lograr es que tenga un método que devuelva "criterios de búsqueda" válidos en formato de cadena, para que pueda usarse fácilmente para todo tipo de tipos de fecha. Si es necesario, puedo hacer una excepción en DateTime con nulos, pero preferiría evitarlo. – Tx3

+0

Tiene razón, por supuesto, sobre InvariantCulture y sobre el código que actualmente es frágil. – Tx3

+0

No se puede hacer. No hay una forma eficiente de buscar, excepto "entre" en los tiempos de fecha en sql, y lo que LINQ puede hacer es limitado aquí. Su enfoque genérico toma en consideración las peculiaridades de LINQ. – TomTom

0

Actualmente resuelvo esto con un bloque try/catch. Eso añade algo de sobrecarga y la fealdad que preferiría encontrar una alternativa para, pero funciona muy sólidamente:

try 
{ 
    // for strings and other non-nullables 
    records = records.Where(rule.field + ".ToString().Contains(@0)", rule.data); 
} 
catch (System.Linq.Dynamic.ParseException) 
{ 
    // null types 
    records = records.Where(rule.field + ".Value.ToString().Contains(@0)", rule.data); 
} 

Nota: La aceptación de los campos de variables de entrada (el rule.field y rule.data) es peligroso, y puede que abrir hasta una Inyección SQL ciega (difícil). El primer valor (rule.field) se puede comparar con una lista blanca de valores para evitar esto.

Cuestiones relacionadas