web-dev-qa-db-fra.com

Comment exécuter une recherche en texte intégral à l'aide du framework entity 6

J'ai la requête:

var query = DataContext.Fotos.Where(x => x.Pesquisa.Contais("myTerm")

Le SQL généré est:

SELECT ... DE Fotos AS [Extent1] WHERE [Ampleur1]. [Pesquisa] LIKE N '% mytem%'

Mais j'ai besoin d'utiliser:

SELECT ... DE Fotos AS [Extent1] WHERE CONTAINS ([Étendue 1]. [Pesquisa], 'mon terme')

Comment exécuter une recherche en texte intégral à l'aide de l'entité framework 6?

18
Eduardo Silva

Apparemment, Entity Framework 6 ne prend pas en charge la recherche de texte intégral, mais il existe une solution de contournement avec les intercepteurs.

http://www.entityframework.info/Home/FullTextSearch

Update Le lien ne fonctionne pas, voici donc le contenu original:

Microsoft TSQL prend en charge les requêtes en texte intégral au moyen de prédicats (CONTIENT et FREETEXT)

Par exemple, vous avez la table Notes

Create table Notes (
    Id int Identity not null,
    NoteText text 
)

CREATE FULLTEXT CATALOG [Notes Data]

Lorsque vous recherchez dans cette table des enregistrements contenant le mot 'John', vous besoin d'émettre

SELECT TOP (10) 
* from gps.NOTES
WHERE contains(NoteText, '(john)') 

Malheureusement, le cadre Enity ne prend pas en charge la recherche de texte intégral prédicats encore. Pour EFv6, vous pouvez créer une solution de contournement à l'aide de interception.

L'idée est d'envelopper le texte de recherche avec un mot magique lors de l'intérieur plain String.Container code et utiliser interceptor pour le dérouler correctement avant que SQL soit exécuté dans SqlCommand.

Pour commencer, créons la classe interceptor:

public class FtsInterceptor : IDbCommandInterceptor
{
    private const string FullTextPrefix = "-FTSPREFIX-";
    public static string Fts(string search)
    {
    return string.Format("({0}{1})", FullTextPrefix, search);
    }
    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }
    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }
    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        RewriteFullTextQuery(command);
    }
    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
    }
    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        RewriteFullTextQuery(command);
    }
    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }
    public static void RewriteFullTextQuery(DbCommand cmd)
    {
        string text = cmd.CommandText;
        for (int i = 0; i < cmd.Parameters.Count; i++)
        {
            DbParameter parameter = cmd.Parameters[i];
            if (parameter.DbType.In(DbType.String, DbType.AnsiString, DbType.StringFixedLength, DbType.AnsiStringFixedLength))
            {
                if (parameter.Value == DBNull.Value)
                    continue;
                var value = (string)parameter.Value;
                if (value.IndexOf(FullTextPrefix) >= 0)
                {
                    parameter.Size = 4096;
                    parameter.DbType = DbType.AnsiStringFixedLength;
                    value = value.Replace(FullTextPrefix, ""); // remove prefix we added n linq query
                    value = value.Substring(1, value.Length - 2); // remove %% escaping by linq translator from string.Contains to sql LIKE
                    parameter.Value = value;
                    cmd.CommandText = Regex.Replace(text,
                    string.Format(
                    @"\[(\w*)\].\[(\w*)\]\s*LIKE\s*@{0}\s?(?:ESCAPE
                    N?'~')",parameter.ParameterName),
                    string.Format(@"contains([$1].[$2], @{0})",parameter.ParameterName));
                    if (text == cmd.CommandText)
                        throw new Exception("FTS was not replaced on: " + text);
                    text = cmd.CommandText;
                }
            }
        }
    }
}

J'ai utilisé la fonction d'extension In qui peut être définie comme suit:

static class LanguageExtensions
{
    public static bool In<T>(this T source, params T[] list)
    {
        return (list as IList<T>).Contains(source);
    }
}

Maintenant, composons un exemple d'utilisation. Nous avons besoin de la classe d'entité Note:

public class Note
{
    public int Id { get; set; }
    public string NoteText { get; set; }
}

Cartographie de configuration pour cela:

public class NoteMap : EntityTypeConfiguration<Note>
{
    public NoteMap()
    {
        // Primary Key
        HasKey(t => t.Id);
    }
}

Et notre ancêtre DbContext:

public class MyContext : DbContext
{
    static MyContext()
    {
        DbInterception.Add(new FtsInterceptor());
    }
    public MyContext(string nameOrConnectionString) : base(nameOrConnectionString)
    {
    }
    public DbSet<Note> Notes { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new NoteMap());
    }
}

Maintenant, nous sommes prêts à l'utiliser. Permet de rechercher 'john':

class Program
{
    static void Main(string[] args)
    {
        var s = FtsInterceptor.Fts("john");
        using (var db = new MyContext("CONNSTRING"))
        {
            var q = db.Notes.Where(n => n.NoteText.Contains(s));
            var result = q.Take(10).ToList();
        }
    }
}
22
Marian Ban

Vous pouvez utiliser des requêtes SQL brutes avec EF. Il existe donc une autre solution de contournement facile.

        using (DBContext context = new DBContext())
        {
            string query = string.Format("Select Id, Name, Description From Fotos Where CONTAINS(Pesquisa, '\"{0}\"')", textBoxStrToSearch.Text);
            var data = context.Database.SqlQuery<Fotos>(query).ToList();
            dataGridView1.DataSource = data;
        }

La validation des entrées, etc. est omise. Edit: Le code est modifié en fonction de la requête de l'OP.

0
Celal Ergün