Pergunta

Gostaria de saber se é possível usar FTS com LINQ usando .NET Framework 3.5. Estou pesquisando em torno da documentação que eu não encontrar nada de útil ainda.

Alguém tem alguma experiência sobre isso?

Foi útil?

Solução

Sim. No entanto, você tem que criar a função de servidor SQL em primeiro lugar e chamada que como por LINQ padrão usará um gosto.

Este post que irá explicar os detalhes, mas esta é a extract:

Para fazê-lo funcionar, você precisa criar uma função com valor de tabela que faz nada mais do que uma consulta CONTAINSTABLE com base nas palavras-chave que você passa em,

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

Em seguida, adicione esta função para o seu modelo de SQL LINQ 2 e ele presto você pode agora consultas escrever como.

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

Outras dicas

No. pesquisa de texto completo não é suportado pelo LINQ to SQL.

Dito isso, você pode usar um procedimento armazenado que utiliza FTS e ter os dados de puxar consulta LINQ to SQL a partir daí.

Eu não acredito que sim. Você pode usar 'contém' em um campo, mas só gera uma consulta LIKE. Se você quiser usar texto completo Eu recomendaria usar uma proc armazenado para fazer a consulta, em seguida, passá-lo de volta para LINQ

Se você não quiser criar junta e quer simplificar o seu código C #, você pode criar funções SQL e usá-lo em "de" cláusula:

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

Depois de atualizar o seu DBML, usá-lo em LINQ:

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

Isto irá produzir SQL simples como isto:

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

Esta é obras em busca de várias colunas como você pode ver a partir da implementação da função ad_Search.

Não, pesquisa de texto completo é algo muito específico para o SQL Server (em que o texto é indexado por palavras, e consultas atingiu este índice em relação atravessando uma matriz de caracteres). não Linq não suporta isso, nenhum .Contains () chama chegará às funções de cadeia conseguiu-un, mas não vai beneficiar de indexação.

Eu fiz um protótipo de trabalho, para o SQL Server do contém e só há colunas curinga. O que ele consegue é para você usar contém como funções LINQ comuns:

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

Você vai precisar de:

1.Function definições no código e EDMX para apoiar o contém palavra-chave.

2.Rewrite EF SQL por EFProviderWrapperToolkit / EFTracingProvider, porque CONTÉM não é uma função e por padrão, o SQL gerado trata seu resultado como pouco .

MAS:

1.Contains não é realmente uma função e não é possível seleccionar resultados boolean dele. Ele só pode ser usado em condições.

2. O SQL código de reescrever a seguir é provável que quebrar se consultas contêm cordas não-parametrizadas com caracteres especiais.

Fonte do meu protótipo

definições de funções: (EDMX)

Sob 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: os casos estranhos de caracteres são usados ??para ativar a mesma função com diferentes tipos de parâmetros (varbinary e nvarchar)

definições de funções: (código)

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" é o mesmo que o valor em edmx: StorageModels / Schema / @ Namespace

Rewrite EF SQL: (por 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;
    }
}

Ativar EFProviderWrapperToolkit:

Se você obtê-lo por NuGet, ele deve adicionar estas linhas no seu app.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>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top