Crear una función de predicado común


En primer lugar, no estoy seguro de qué términos usar para hacer esta pregunta, que es probablemente la razón por la que no he encontrado una respuesta al buscarme a mí mismo.

Así que estoy trabajando con Linq to SQL (C#,. Net 4) y quiero obtener una lista de todos los usuarios que coinciden con un criterio, los fundamentos de los cuales haría algo como esto:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");

Pero en este caso hay algunos campos que quiero que coincidan, la cosa es que estos campos en particular son una comprobación común y me gustaría poder crear una dedicatoria función que puedo usar dentro de cualquiera de mis consultas de usuario para verificar esta coincidencia.

Para tratar de explicar que un poco mejor vamos a dar un ejemplo: Digamos que un usuario tiene 5 banderas, y quiero una comprobación común para ver si cualquier de esas banderas se establecen. Así que podría escribir mi consulta así:

var users = DataContext.Users.Where(x => x.Flag1 || x.Flag2 || x.Flag3 || x.Flag4 || x.Flag5);

Pero lo que me gustaría hacer es separar ese "5 flag check" para que pueda usarlo en otras consultas también, en última instancia, me gustaría usar algo como:

var users = DataContext.Users.Where(x => x.Criteria1 == "something" && CheckForFlags(x));

He intentado esto por tener una función como esta:

static bool CheckForFlags(User user)
{
   return user.Flag1 || user.Flag2 || user.Flag3 || user.Flag4 || user.Flag5;
}

Pero obtengo un error:

"El método 'Boolean CheckForFlags(User)' no tiene traducción soportada a SQL."

...lo cual tiene sentido, pero ¿hay algo que pueda hacer para que esto funcione de la manera que quiero? ¿O es esto una restricción porque estoy usando Linq to SQL y es de hecho algo que funcionaría con Linq to Objects?

Author: Mario Sannum, 2012-11-01

5 answers

Lo bueno de cómo LINQ to SQL maneja las expresiones es que en realidad puede construir expresiones en otro lugar de su código y hacer referencia a ellas en sus consultas. ¿Por qué no intentas algo como esto:

public static class Predicates
{
    public static Expression<Func<User, bool>> CheckForFlags()
    {
        return (user => user.Flag1 || user.Flag2 || user.Flag3 ||
                        user.Flag4 || user.Flag5);
    }

    public static Expression<Func<User, bool>> CheckForCriteria(string value)
    {
        return (user => user.Criteria1 == value);
    }
}

Una vez que haya definido sus predicados, es muy fácil usarlos en una consulta.

var users = DataContext.Users
    .Where(Predicates.CheckForFlags())
    .Where(Predicates.CheckForCriteria("something"));
 39
Author: Adam Maras,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-01 16:24:06

¿Has probado PredicateBuilder? No lo he usado en más de un año, pero lo encontré efectivo al escribir consultas "O Dónde".

Http://www.albahari.com/nutshell/predicatebuilder.aspx

Un ejemplo de su página:

IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}
 4
Author: JoshVarty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-01 16:10:49

A mi leal saber y entender, hay dos maneras posibles de hacer esto.

La forma quick-n-easy de filtrar los resultados después de ejecutar SQL, con algo como esto:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");
    .ToEnumerable()
    .Where(x => CheckForFlags(x));

Sin embargo, esto es muy pobre en términos de rendimiento. Devolverá TODAS las filas de la base de datos que coincidan solo con los primeros criterios, y luego filtrará los resultados en memoria en el cliente. Funcional, pero lejos de ser óptimo.

La segunda opción, mucho más eficaz es crear un UDF en la base de datos en sí y llamarla desde LINQ. Ver por ejemplo esta pregunta. La desventaja obvia es que mueve el código a la base de datos, lo que a nadie le gusta hacer (por muchas razones válidas).

Bien puede haber otras soluciones viables, pero esas son las únicas dos que conozco.

 0
Author: ean5533,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:17

Yo creo que esto funcionará, y yo creo que lo he usado en mi proyecto Linq-to-SQL, pero no puedo encontrar un ejemplo de inmediato. Si no, avísame.


En lugar de crear una función, cree una nueva propiedad en el objeto Users:

partial class Users {
   bool CheckForFlags
    {
       get { 
          return Flag1 || Flag2 || Flag3 || Flag4 || Flag5;
       }
    }
}

Entonces usted debe ser capaz de hacer

var users = DataContext.Users.Where(x => x.CheckForFlags);
 0
Author: Bobson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-01 16:16:03

Como dijiste, esta es una restricción porque estás usando Linq para SQL.

Algo como esto debería funcionar:

var users = DataContext.Users.Where(x => x.Criteria1 == "something")
                             .ToArray()
                             .Where(x => CheckForFlags(x));
 -1
Author: RobSiklos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-01 16:04:16