Question

Je me demande s’il est possible d’utiliser FTS avec LINQ avec .NET Framework 3.5. Je cherche dans la documentation mais je n'ai encore rien trouvé d'utile.

Quelqu'un at-il une expérience dans ce domaine?

Était-ce utile?

La solution

Oui. Cependant, vous devez d'abord créer une fonction serveur SQL et l'appeler comme par défaut, LINQ utilisera la même chose.

Cette article de blog qui expliquera les détails mais voici l'extrait:

  

Pour que cela fonctionne, vous devez créer une fonction de valeur table qui   rien de plus qu'une requête CONTAINSTABLE basée sur les mots-clés que vous passez   dans,

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

Vous ajoutez ensuite cette fonction à votre modèle SQL LINQ 2 et il vous prépare   peut maintenant écrire des requêtes comme.

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

Autres conseils

Non. La recherche en texte intégral n'est pas prise en charge par LINQ To SQL.

Cela dit, vous pouvez utiliser une procédure stockée utilisant FTS et demander à la requête LINQ To SQL d'extraire des données de celle-ci.

Je ne crois pas. Vous pouvez utiliser 'contient' sur un champ, mais il ne génère qu'une requête LIKE . Si vous souhaitez utiliser le texte intégral, je vous recommande d'utiliser un proc stocké pour effectuer la requête, puis le renvoyer à LINQ

si vous ne souhaitez pas créer de jointure et que vous souhaitez simplifier votre code C #, vous pouvez créer une fonction SQL et l'utiliser dans " à partir de " clause:

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

Après avoir mis à jour votre DBML, utilisez-le dans linq:

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

Ceci produira un SQL simple comme ceci:

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

Ceci fonctionne dans la recherche par plusieurs colonnes, comme vous pouvez le voir à partir de l'implémentation de la fonction ad_Search.

Non, la recherche en texte intégral est quelque chose de très spécifique au serveur SQL (dans lequel le texte est indexé par des mots et les requêtes atteignent cet index par opposition à un tableau de caractères). Linq ne le prend pas en charge, tous les appels .Contains () atteindront les fonctions de chaîne non gérées mais ne bénéficieront pas de l'indexation.

J'ai créé un prototype fonctionnel, uniquement pour les colonnes CONTAINS de SQL Server, sans colonnes génériques. L’utilisation de CONTAINS est semblable à celle des fonctions LINQ ordinaires:

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

Vous aurez besoin de:

1.Fonctions définies dans le code et dans EDMX pour prendre en charge le mot clé CONTAINS .

2.Récrivez EF SQL par EFProviderWrapperToolkit / EFTracingProvider, car CONTAINS n'est pas une fonction et par défaut, le code SQL généré traite son résultat comme bit .

MAIS:

1.Contains n'est pas vraiment une fonction et vous ne pouvez pas en sélectionner des résultats booléens. Il ne peut être utilisé que dans des conditions.

2.Le code de réécriture SQL ci-dessous risque de s'interrompre si les requêtes contiennent des chaînes non paramétrées avec des caractères spéciaux.

Source de mon prototype

Définitions de fonctions: (EDMX)

Sous 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: les cas étranges de caractères sont utilisés pour activer la même fonction avec différents types de paramètres (varbinary et nvarchar)

Définitions de fonctions: (code)

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" est identique à la valeur de edmx: StorageModels / Schema / @ Namespace

Réécrire EF SQL: (par 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;
    }
}

Activer EFProviderWrapperToolkit:

Si vous l'obtenez par nuget, il devrait ajouter ces lignes dans votre application.config ou 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>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top