String.IsNullOrWhiteSpace en LINQ Expression

Tengo el siguiente código:

return this.ObjectContext.BranchCostDetails.Where( b => b.TarrifId == tariffId && b.Diameter == diameter || (b.TarrifId==tariffId && !string.IsNullOrWhiteSpace(b.Diameter)) || (!b.TarrifId.HasValue) && b.Diameter==diameter); 

Y obtengo este error cuando bash ejecutar el código:

LINQ to Entities no reconoce el método ‘Boolean IsNullOrWhiteSpace (System.String)’, y este método no se puede traducir a una expresión de tienda. ”

¿Cómo puedo resolver este problema y escribir código mejor que esto?

Debes reemplazar

 !string.IsNullOrWhiteSpace(b.Diameter) 

con

 !(b.Diameter == null || b.Diameter.Trim() == string.Empty) 

Para Linq to Entities, esto se traduce en:

 DECLARE @p0 VarChar(1000) = '' ... WHERE NOT (([t0].[Diameter] IS NULL) OR (LTRIM(RTRIM([t0].[Diameter])) = @p0)) 

y para Linq a SQL casi, pero no del todo lo mismo

 DECLARE @p0 NVarChar(1000) = '' ... WHERE NOT (LTRIM(RTRIM([t0].[TypeName])) = @p0) 

En este caso, es importante distinguir entre IQueryable e IEnumerable . En resumen, IQueryable es procesado por un proveedor de LINQ para entregar una consulta optimizada. Durante esta transformación, no todas las sentencias C # son compatibles, ya que no es posible traducirlas a una consulta específica de back-end (por ejemplo, SQL) o porque el implementador no previó la necesidad de la statement.

Por el contrario, IEnumerable se ejecuta contra los objetos concretos y, por lo tanto, no se transformará. Por lo tanto, es bastante común que las construcciones que se pueden usar con IQueryable no se puedan usar con IQueryable y que las IQueryables respaldadas por diferentes proveedores de LINQ no admitan el mismo conjunto de funciones.

Sin embargo, hay algunas soluciones (como la respuesta de Phil ) que modifican la consulta. Además, como un enfoque más general, es posible regresar a un IEnumerable antes de continuar con la especificación de la consulta. Sin embargo, esto podría tener un impacto en el rendimiento, especialmente cuando se usa con restricciones (por ejemplo, cláusulas where). Por el contrario, cuando se trata de transformaciones, el rendimiento alcanzado es mucho menor, a veces incluso inexistente, dependiendo de su consulta.

Entonces, el código anterior también podría reescribirse así:

 return this.ObjectContext.BranchCostDetails .AsEnumerable() .Where( b => b.TarrifId == tariffId && b.Diameter == diameter || (b.TarrifId==tariffId && !string.IsNullOrWhiteSpace(b.Diameter)) ||(!b.TarrifId.HasValue) && b.Diameter==diameter ); 

NOTA: Este código tendrá un mayor impacto en el rendimiento que la respuesta de Phil . Sin embargo, muestra el principio.

Utilice un visitante de expresión para detectar referencias a string.IsNullOrWhiteSpace y descomponerlas en una expresión más simple (x == null || x.Trim() == string.Empty) .

A continuación se muestra un visitante extendido y un método de extensión para hacer uso de él. No requiere configuración especial para usar, simplemente llame a WhereEx en lugar de Where.

 public class QueryVisitor: ExpressionVisitor { protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Method.IsStatic && node.Method.Name == "IsNullOrWhiteSpace" && node.Method.DeclaringType.IsAssignableFrom(typeof(string))) { //!(b.Diameter == null || b.Diameter.Trim() == string.Empty) var arg = node.Arguments[0]; var argTrim = Expression.Call(arg, typeof (string).GetMethod("Trim", Type.EmptyTypes)); var exp = Expression.MakeBinary(ExpressionType.Or, Expression.MakeBinary(ExpressionType.Equal, arg, Expression.Constant(null, arg.Type)), Expression.MakeBinary(ExpressionType.Equal, argTrim, Expression.Constant(string.Empty, arg.Type)) ); return exp; } return base.VisitMethodCall(node); } } public static class EfQueryableExtensions { public static IQueryable WhereEx(this IQueryable queryable, Expression> where) { var visitor = new QueryVisitor(); return queryable.Where(visitor.VisitAndConvert(where, "WhereEx")); } } 

Entonces, si ejecuta myqueryable.WhereEx(c=> !c.Name.IsNullOrWhiteSpace()) se convertirá en !(c.Name == null || x.Trim() == "") antes de pasar a lo que sea (linq a sql / entidades) y convertido a sql.

También puede usar esto para verificar espacios en blanco:

 !(String.IsNullOrEmpty(b.Diameter.Trim());