Pregunta sobre linq-to-entities, .net, sql-server, entity-framework – ¿Como Operador en Entity Framework?

79

Estamos tratando de implementar el operador "LIKE" en Entity Framework para nuestras entidades con campos de cadena, pero no parece ser compatible. ¿Alguien más ha intentado hacer algo como esto?

Estaentrada en el blog Resume el problema que estamos teniendo. Podríamos usar contiene, pero eso solo coincide con el caso más trivial para LIKE. Combinar contiene, comienza con, final con e indexof nos lleva allí, pero requiere una traducción entre los comodines estándar y el código Linq to Entities.

Iresta respuesta  Si ya está utilizando EF 6.2.x. Aesta respuesta Si estás utilizando EF Core 2.x CodeNotFound

Tu respuesta

9   la respuesta
0

bla foobar foo? bar? foo * bar? y otros patrones complejos. "No he intentado esto (no lo he necesitado todavía), pero ¿ha intentado usar System.Text.RegularExpressions.RegEx?

Una expresión regular se ejecutaría en el servidor web, no en la base de datos. Básicamente, implicaría devolver la tabla completa desde SQL Server y luego filtrar. Eso funcionaría para algunos casos pequeños, pero necesitamos buscar grandes volúmenes de datos (ish). Buena sugerencia, sin embargo, ese fue mi primer pensamiento también. brien
32

Por ahora, me he conformado con el filtrado de comodines / expresiones regulares del lado del cliente basado enhttp://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - Es simple y funciona como se espera.

He encontrado otra discusión sobre este tema:http://forums.asp.net/t/1654093.aspx/2/10
Esta publicación parece prometedora si usa Entity Framework> = 4.0:

Utilice SqlFunctions.PatIndex:

http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx

Me gusta esto:

<code>var q = EFContext.Products.Where(x =>
SqlFunctions.PatIndex("%CD%BLUE%", x.ProductName) > 0);
</code>

Nota: esta solución es solo para SQL-Server, ya que utiliza la función PATINDEX no estándar.

Sin embargo, @surfen patindex es peor que eso, no usará el índice incluso sin% al frente, la búsqueda de (BLUE CD%) con patindex no usará el índice de la columna. BlackICE
Mientras PatIndex "funciona", volverá a morderte, PatIndex en la cláusula where no usa los índices en la columna en la que te gustaría filtrar. BlackICE
@BlackICE se espera esto. Cuando busca en el texto interno (% CD% BLUE%), el servidor no podrá usar índices. Siempre que sea posible, la búsqueda de texto desde el principio (CD% BLUE%) es más eficiente. surfen
trabajó un regalo, gracias por encontrar esto! Tom Carver
144

pero en LINQ to SQL usualmente expresas una cláusula LIKE usando String. Contiene:

<code>where entity.Name.Contains("xyz")
</code>

traduce a

<code>WHERE Name LIKE '%xyz%'
</code>

(UtilizarStartsWith yEndsWith para otro comportamiento.)

No estoy completamente seguro de si eso es útil, porque no entiendo a qué te refieres cuando dices que estás intentandoimplementar ME GUSTA. Si no he entendido bien, avíseme y eliminaré esta respuesta :)

@landsteven: Eso no responde a mi pregunta. Seguramente si el valor de registro comienza con la entrada del usuario, tambiéncontiene la entrada del usuario. Así que "ABC Dealer X" contiene "ABC Dealer", al igual que "X ABC Dealer". ¿Puede dar un ejemplo en el que Contains devolvería false, pero StartsWith o EndsWith devolverían true? Jon Skeet
@AlexKuznetsov: me parece recordar haber tenido algunos problemas con la compatibilidad entre SQL Server y Oracle en el pasado, especialmente cuando se trata de escapar. Tuve que escribir un operador de Hibernate para hacer frente al% en la cadena de consulta, y las implementaciones de Oracle y SQL Server fueron diferentes. Podría ser que me faltara un truco, o que el mundo haya mejorado desde entonces. Jon Skeet
@John Skeet: cuando mi usuario ingresa "ABC Dealer" como la cadena para consultar mi base de datos para devolver los resultados de un AutoCompleteBox.ItemsSource. Los resultados deben incluir una lista de todos los registros relativos encontrados en la tabla de origen, incluso cuando el registro real que están buscando puede comenzar O contener O terminar con la entrada proporcionada. landsteven
Sí, esta es la solución para el equivalente de SQL LIKE: .Contains (valor) || .StartsWith (valor) || .EndsWith (valor) landsteven
0

Añadir

<code>    <Function Name="String_Like" ReturnType="Edm.Boolean">
      <Parameter Name="searchingIn" Type="Edm.String" />
      <Parameter Name="lookingFor" Type="Edm.String" />
      <DefiningExpression>
        searchingIn LIKE lookingFor
      </DefiningExpression>
    </Function>
</code>

a su EDMX en esta etiqueta:

edmx: Edmx / edmx: Runtime / edmx: ConceptualModels / Schema

También recuerda el espacio de nombres en el<schema namespace="" /> atributo

Luego agregue una clase de extensión en el espacio de nombres anterior:

<code>public static class Extensions
{
    [EdmFunction("DocTrails3.Net.Database.Models", "String_Like")]
    public static Boolean Like(this String searchingIn, String lookingFor)
    {
        throw new Exception("Not implemented");
    }
}
</code>

Este método de extensión ahora se asignará a la función EDMX.

Más información aquí:http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html

28

pero para cualquiera que busque la respuesta,este enlace debería ayudar. Iresta respuesta Si ya está utilizando EF 6.2.x. Aesta respuesta Si estás utilizando EF Core 2.x

Version corta:

SqlFunctions.PatIndex Método: devuelve la posición inicial de la primera aparición de un patrón en una expresión especificada, o ceros si no se encuentra el patrón, en todos los tipos de datos de texto y caracteres válidos.

Espacio de nombres: System.Data.Objects.SqlClient Ensamblado: System.Data.Entity (en System.Data.Entity.dll)

Un poco de explicación también aparece en estehilo del foro.

Dup de este mayorresponder a esta pregunta (No de su primera parte, sino de su solución alternativa.) Frédéric
La respuesta a continuación es agradable para patrones simples, pero si quiero decir "DÓNDE Nombre LIKE 'abc [0-9]%'" o algún otro patrón más complejo, simplemente usar Contains () no es suficiente. HotN
La respuesta fue usar el método SqlFunctions.PatIndex. El hilo del foro vinculado era proporcionar un poco más de información "de fondo". Yann Duran
¿Cómo es la respuesta aceptada la que enlaza con un foro de MSDN que enlaza con esta pregunta a laResponda abajo? Eonasdan
9

Ahi estaLIKE el operador se agrega enEntity Framework Core 2.0:

<code>var query = from e in _context.Employees
                    where EF.Functions.Like(e.Title, "%developer%")
                    select e;
</code>

Comparado a... where e.Title.Contains("developer") ... es realmente traducido aSQL LIKE más bien queCHARINDEX vemos porContains método.

14

Actualización: En EF 6.2 hay un operador similar.

<code>Where(i => DbFunctions.Like(searchstring ,like expression)
</code>
2

escribí 2 métodos de extensión para admitir el carácter% para la búsqueda con comodines. (Se requiere LinqKit)

<code>public static class ExpressionExtension
{
    public static Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> expr, string likeValue)
    {
        var paramExpr = expr.Parameters.First();
        var memExpr = expr.Body;

        if (likeValue == null || likeValue.Contains('%') != true)
        {
            Expression<Func<string>> valExpr = () => likeValue;
            var eqExpr = Expression.Equal(memExpr, valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
        }

        if (likeValue.Replace("%", string.Empty).Length == 0)
        {
            return PredicateBuilder.True<T>();
        }

        likeValue = Regex.Replace(likeValue, "%+", "%");

        if (likeValue.Length > 2 && likeValue.Substring(1, likeValue.Length - 2).Contains('%'))
        {
            likeValue = likeValue.Replace("[", "[[]").Replace("_", "[_]");
            Expression<Func<string>> valExpr = () => likeValue;
            var patExpr = Expression.Call(typeof(SqlFunctions).GetMethod("PatIndex",
                new[] { typeof(string), typeof(string) }), valExpr.Body, memExpr);
            var neExpr = Expression.NotEqual(patExpr, Expression.Convert(Expression.Constant(0), typeof(int?)));
            return Expression.Lambda<Func<T, bool>>(neExpr, paramExpr);
        }

        if (likeValue.StartsWith("%"))
        {
            if (likeValue.EndsWith("%") == true)
            {
                likeValue = likeValue.Substring(1, likeValue.Length - 2);
                Expression<Func<string>> valExpr = () => likeValue;
                var containsExpr = Expression.Call(memExpr, typeof(String).GetMethod("Contains",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(containsExpr, paramExpr);
            }
            else
            {
                likeValue = likeValue.Substring(1);
                Expression<Func<string>> valExpr = () => likeValue;
                var endsExpr = Expression.Call(memExpr, typeof(String).GetMethod("EndsWith",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(endsExpr, paramExpr);
            }
        }
        else
        {
            likeValue = likeValue.Remove(likeValue.Length - 1);
            Expression<Func<string>> valExpr = () => likeValue;
            var startsExpr = Expression.Call(memExpr, typeof(String).GetMethod("StartsWith",
                new[] { typeof(string) }), valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(startsExpr, paramExpr);
        }
    }

    public static Expression<Func<T, bool>> AndLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var andPredicate = Like(expr, likeValue);
        if (andPredicate != null)
        {
            predicate = predicate.And(andPredicate.Expand());
        }
        return predicate;
    }

    public static Expression<Func<T, bool>> OrLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var orPredicate = Like(expr, likeValue);
        if (orPredicate != null)
        {
            predicate = predicate.Or(orPredicate.Expand());
        }
        return predicate;
    }
}
</code>

uso

<code>var orPredicate = PredicateBuilder.False<People>();
orPredicate = orPredicate.OrLike(per => per.Name, "He%llo%");
orPredicate = orPredicate.OrLike(per => per.Name, "%Hi%");

var predicate = PredicateBuilder.True<People>();
predicate = predicate.And(orPredicate.Expand());
predicate = predicate.AndLike(per => per.Status, "%Active");

var list = dbContext.Set<People>().Where(predicate.Expand()).ToList();    
</code>

en ef6 y debería traducirse a

<code>....
from People per
where (
    patindex(@p__linq__0, per.Name) <> 0
    or per.Name like @p__linq__1 escape '~'
) and per.Status like @p__linq__2 escape '~'
</code>

', @ p__linq__0 ='% He% llo% ', @ p__linq__1 ='% Hi% ', @ p__linq_2 ='% activo '

Tengo error en PredicateBuilder Ronel Gonzales
Gracias por tu comentario Ronel, ¿hay algo en lo que pueda ayudarte? cual es el mensaje de error Steven Chong
5

QL. ¿Está recibiendo un mensaje de error?

<code>// LIKE and ESCAPE
// If an AdventureWorksEntities.Product contained a Name 
// with the value 'Down_Tube', the following query would find that 
// value.
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name LIKE 'DownA_%' ESCAPE 'A'

// LIKE
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name like 'BB%'
</code>

http://msdn.microsoft.com/en-us/library/bb399359.aspx

Parece que Contains () es tu boleto. Pero como señaló Jon Skeet, es posible que tenga que desplegar algún tipo de SQL real que manipule la base de datos directamente, si Contains no satisface sus necesidades. Robert Harvey
Otra publicación de StackOverflow relevante:stackoverflow.com/questions/835790/how-to-do-sql-like-in-linq Robert Harvey
Edité la pregunta con un enlace a una publicación del blog que describe el mismo problema que estamos teniendo. brien
Estaría tentado a mantenerme alejado de Entity SQL en caso de que quisiera alejarse de EF en el futuro. Juegue de forma segura y siga con las opciones Contains (), StartsWith () y EndsWith () en la respuesta original. Stephen Newman

Preguntas relacionadas