Domanda

Mi chiedo se è possibile usare FTS con LINQ usando .NET Framework 3.5. Sto cercando nella documentazione che non ho ancora trovato nulla di utile.

Qualcuno ha qualche esperienza su questo?

È stato utile?

Soluzione

Sì. Tuttavia devi prima creare la funzione del server SQL e chiamarla come per impostazione predefinita LINQ utilizzerà un like.

Questo post sul blog che spiegherà i dettagli ma questo è l'estratto:

  

Per farlo funzionare è necessario creare una funzione con valori di tabella che funzioni   nient'altro che una query CONTAINSTABLE basata sulle parole chiave che passi   a,

create function udf_sessionSearch
      (@keywords nvarchar(4000))
returns table
as
  return (select [SessionId],[rank]
            from containstable(Session,(description,title),@keywords))
     

Quindi aggiungi questa funzione al tuo modello LINQ 2 SQL e presto te   ora può scrivere query come.

    var sessList = from s   in DB.Sessions
                   join fts in DB.udf_sessionSearch(SearchText) 
                   on s.sessionId equals fts.SessionId
                 select s;

Altri suggerimenti

No. La ricerca di testo completo non è supportata da LINQ To SQL.

Detto questo, puoi utilizzare una procedura memorizzata che utilizza FTS e fare in modo che la query LINQ To SQL ne trai i dati.

Non ci credo. Puoi utilizzare "contiene" in un campo, ma genera solo una query LIKE . Se si desidera utilizzare il testo completo, si consiglia di utilizzare un proc memorizzato per eseguire la query, quindi passarlo a LINQ

se non si desidera creare join e si desidera semplificare il codice C #, è possibile creare la funzione SQL e utilizzarlo in " da " Clausola:

CREATE FUNCTION ad_Search
(
      @keyword nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(
      select * from Ad where 
      (CONTAINS(Description, @keyword) OR CONTAINS(Title, @keyword))
)

Dopo aver aggiornato il tuo DBML, usalo in linq:

string searchKeyword = "word and subword";
var result = from ad in context.ad_Search(searchKeyword)
                 select ad;

Questo produrrà un SQL semplice come questo:

SELECT [t0].ID, [t0].Title, [t0].Description
FROM [dbo].[ad_Search](@p0) AS [t0]

Funziona nella ricerca di più colonne come puoi vedere dall'implementazione della funzione ad_Search.

No, la ricerca full-text è qualcosa di molto specifico per il server sql (in cui il testo è indicizzato da parole e le query raggiungono questo indice invece di attraversare una matrice di caratteri). Linq non lo supporta, tutte le chiamate .Contains () colpiranno le funzioni di stringa non gestite ma non beneficeranno dell'indicizzazione.

Ho realizzato un prototipo funzionante, solo per CONTAINS di SQL Server e senza colonne jolly. Ciò che ottiene è che tu usi CONTAINS come le normali funzioni LINQ:

var query = context.CreateObjectSet<MyFile>()
    .Where(file => file.FileName.Contains("pdf")
        && FullTextFunctions.ContainsBinary(file.FileTable_Ref.file_stream, "Hello"));

Dovrai:

1.Funzione definizioni nel codice e EDMX per supportare la parola chiave CONTAINS .

2.Riscrivi EF SQL di EFProviderWrapperToolkit / EFTracingProvider, perché CONTAINS non è una funzione e per impostazione predefinita l'SQL generato considera il suo risultato come bit .

MA:

1.Contains non è realmente una funzione e non è possibile selezionare risultati booleani da essa. Può essere utilizzato solo in condizioni.

2. È probabile che il codice di riscrittura SQL riportato sotto si interrompa se le query contengono stringhe non parametrizzate con caratteri speciali.

Fonte del mio prototipo

Definizioni delle funzioni: (EDMX)

In edmx: StorageModels / Schema

<Function Name="conTAINs" BuiltIn="true" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" ReturnType="bit" Schema="dbo">
    <Parameter Name="dataColumn" Type="varbinary" Mode="In" />
    <Parameter Name="keywords" Type="nvarchar" Mode="In" />
</Function>
<Function Name="conTAInS" BuiltIn="true" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" ReturnType="bit" Schema="dbo">
    <Parameter Name="textColumn" Type="nvarchar" Mode="In" />
    <Parameter Name="keywords" Type="nvarchar" Mode="In" />
</Function>

PS: gli strani casi di caratteri sono usati per abilitare la stessa funzione con diversi tipi di parametri (varbinary e nvarchar)

Definizioni delle funzioni: (codice)

using System.Data.Objects.DataClasses;

public static class FullTextFunctions
{
    [EdmFunction("MyModel.Store", "conTAINs")]
    public static bool ContainsBinary(byte[] dataColumn, string keywords)
    {
        throw new System.NotSupportedException("Direct calls are not supported.");
    }

    [EdmFunction("MyModel.Store", "conTAInS")]
    public static bool ContainsString(string textColumn, string keywords)
    {
        throw new System.NotSupportedException("Direct calls are not supported.");
    }
}

PS: " MyModel.Store " è uguale al valore in edmx: StorageModels / Schema / @ Namespace

Riscrivi EF SQL: (di EFProviderWrapperToolkit)

using EFProviderWrapperToolkit;
using EFTracingProvider;

public class TracedMyDataContext : MyDataContext
{
    public TracedMyDataContext()
        : base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
            "name=MyDataContext", "EFTracingProvider"))
    {
        var tracingConnection = (EFTracingConnection) ((EntityConnection) Connection).StoreConnection;
        tracingConnection.CommandExecuting += TracedMyDataContext_CommandExecuting;
    }

    protected static void TracedMyDataContext_CommandExecuting(object sender, CommandExecutionEventArgs e)
    {
        e.Command.CommandText = FixFullTextContainsBinary(e.Command.CommandText);
        e.Command.CommandText = FixFullTextContainsString(e.Command.CommandText);
    }


    private static string FixFullTextContainsBinary(string commandText, int startIndex = 0)
    {
        var patternBeg = "(conTAINs(";
        var patternEnd = ")) = 1";
        var exprBeg = commandText.IndexOf(patternBeg, startIndex, StringComparison.Ordinal);
        if (exprBeg == -1)
            return commandText;
        var exprEnd = FindEnd(commandText, exprBeg + patternBeg.Length, ')');
        if (commandText.Substring(exprEnd).StartsWith(patternEnd))
        {
            var newCommandText = commandText.Substring(0, exprEnd + 2) + commandText.Substring(exprEnd + patternEnd.Length);
            return FixFullTextContainsBinary(newCommandText, exprEnd + 2);
        }
        return commandText;
    }

    private static string FixFullTextContainsString(string commandText, int startIndex = 0)
    {
        var patternBeg = "(conTAInS(";
        var patternEnd = ")) = 1";
        var exprBeg = commandText.IndexOf(patternBeg, startIndex, StringComparison.Ordinal);
        if (exprBeg == -1)
            return commandText;
        var exprEnd = FindEnd(commandText, exprBeg + patternBeg.Length, ')');
        if (exprEnd != -1 && commandText.Substring(exprEnd).StartsWith(patternEnd))
        {
            var newCommandText = commandText.Substring(0, exprEnd + 2) + commandText.Substring(exprEnd + patternEnd.Length);
            return FixFullTextContainsString(newCommandText, exprEnd + 2);
        }
        return commandText;
    }

    private static int FindEnd(string commandText, int startIndex, char endChar)
    {
        // TODO: handle escape chars between parens/squares/quotes
        var lvlParan = 0;
        var lvlSquare = 0;
        var lvlQuoteS = 0;
        var lvlQuoteD = 0;
        for (var i = startIndex; i < commandText.Length; i++)
        {
            var c = commandText[i];
            if (c == endChar && lvlParan == 0 && lvlSquare == 0
                && (lvlQuoteS % 2) == 0 && (lvlQuoteD % 2) == 0)
                return i;
            switch (c)
            {
                case '(':
                    ++lvlParan;
                    break;
                case ')':
                    --lvlParan;
                    break;
                case '[':
                    ++lvlSquare;
                    break;
                case ']':
                    --lvlSquare;
                    break;
                case '\'':
                    ++lvlQuoteS;
                    break;
                case '"':
                    ++lvlQuoteD;
                    break;
            }
        }
        return -1;
    }
}

Abilita EFProviderWrapperToolkit:

Se lo ottieni tramite nuget, dovrebbe aggiungere queste righe nel tuo app.config o web.config:

<system.data>
    <DbProviderFactories>
        <add name="EFTracingProvider" invariant="EFTracingProvider" description="Tracing Provider Wrapper" type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
        <add name="EFProviderWrapper" invariant="EFProviderWrapper" description="Generic Provider Wrapper" type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
    </DbProviderFactories>
</system.data>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top