c# - features - String.IsNullOrWhiteSpace en LINQ Expression
isnullorwhitespace c# (4)
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 intento 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<T>
e IEnumerable<T>
. En resumen, IQueryable<T>
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 declaración.
Por el contrario, IEnumerable<T>
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<T>
no se puedan usar con IQueryable<T>
y que las IQueryables<T>
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<T>
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.
También puede usar esto para verificar espacios en blanco:
!(String.IsNullOrEmpty(b.Diameter.Trim());
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<T> WhereEx<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> 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.