Ist es möglich, Volltextsuche (FTS) mit LINQ zu benutzen?
-
03-07-2019 - |
Frage
Ich frage mich, wenn möglich ist FTS mit LINQ zu verwenden .NET 3.5 Framework. Ich bin rund um die Dokumentation zu suchen, die ich noch nicht nützlich etwas gefunden hat.
Hat jemand Erfahrung auf diesem?
Lösung
Ja. Allerdings müssen Sie SQL-Server-Funktion zuerst und rufen Sie erstellen, die als standardmäßig wird LINQ ein ähnliches verwenden.
Das Blog-Post, die das Detail, aber das ist der Extrakt wird erklärt:
es zum Laufen bringen Sie eine Tabelle bewertet Funktion erstellen müssen, tut nichts anderes als eine CONTAINS Abfrage auf der Basis der Keywords, die Sie weitergeben in,
create function udf_sessionSearch (@keywords nvarchar(4000)) returns table as return (select [SessionId],[rank] from containstable(Session,(description,title),@keywords))
Sie dann diese Funktion, um SQL-Modell LINQ 2 hinzufügen und er presto Sie können nun Anfragen wie schreiben.
var sessList = from s in DB.Sessions join fts in DB.udf_sessionSearch(SearchText) on s.sessionId equals fts.SessionId select s;
Andere Tipps
Nein. Volltextsuche wird nicht von LINQ to SQL unterstützt.
Das heißt, Sie können eine gespeicherte Prozedur, die FTS und haben die LINQ to SQL-Abfrage Pull Daten aus, dass verwendet.
Ich glaube nicht. Sie können ‚enthält‘ auf einem Feld verwenden, aber es erzeugt nur eine LIKE
Abfrage. Wenn Sie Volltext verwenden möchten, würde ich die Abfrage mit einem gespeicherten proc empfehlen zu tun, dann ist es zu LINQ geben zurück
Wenn Sie nicht wollen, schließt sich erstellen und möchten Ihre C # -Code vereinfachen, können Sie SQL-Funktion erstellen und verwenden Sie es in „von“ Klausel:
CREATE FUNCTION ad_Search
(
@keyword nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(
select * from Ad where
(CONTAINS(Description, @keyword) OR CONTAINS(Title, @keyword))
)
Nach dem DBML aktualisieren, verwenden Sie es in Linq:
string searchKeyword = "word and subword";
var result = from ad in context.ad_Search(searchKeyword)
select ad;
Dies wird produzieren einfache SQL wie folgt aus:
SELECT [t0].ID, [t0].Title, [t0].Description
FROM [dbo].[ad_Search](@p0) AS [t0]
Dies ist Werk auf der Suche nach mehreren Spalten, wie Sie aus der ad_Search Funktion Implementierung sehen können.
Nein, Volltextsuche ist etwas sehr spezifisch für SQL Server (in diesem Text wird durch Worte indiziert und Abfragen schlugen diesen Index einen Zeichen-Array im Vergleich zu durchqueren). Linq dies nicht unterstützt, werden alle .Contains () ruft werden die un-verwalteten String-Funktionen schlagen, aber nicht von der Indizierung profitieren.
Ich habe einen funktionierenden Prototyp, für SQL Server ENTHÄLT nur und keine Wildcard-Spalten. Was sie erreicht, ist für Sie zu nutzen ENTHÄLT wie gewöhnliche LINQ Funktionen:
var query = context.CreateObjectSet<MyFile>()
.Where(file => file.FileName.Contains("pdf")
&& FullTextFunctions.ContainsBinary(file.FileTable_Ref.file_stream, "Hello"));
Sie benötigen:
1.Function Definitionen in Code und EDMX die Unterstützung ENTHÄLT Stichwort.
2.Rewrite EF SQL durch EFProviderWrapperToolkit / EFTracingProvider, weil ENTHÄLT keine Funktion und wird standardmäßig die erzeugte SQL behandelt sein Ergebnis als Bit .
ABER:
1.Contains ist nicht wirklich eine Funktion und man kann nicht boolean Ergebnisse auswählen. Es kann nur unter Bedingungen verwendet werden.
2. SQL Umschreiben Code unten ist wahrscheinlich zu brechen, wenn Abfragen enthalten nichtparametrisierter Strings mit Sonderzeichen.
Quelle meines Prototyp
Funktionsdefinitionen: (EDMX)
Unter 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: die seltsamen Fälle von Zeichen verwendet werden, um die gleiche Funktion mit verschiedenen Parametertypen (varbinary und nvarchar)
aktivierenFunktionsdefinitionen: (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" ist das gleiche wie der Wert in edmx: StorageModels / Schema / @ Namespace
Rewrite EF SQL: (von 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;
}
}
Aktivieren EFProviderWrapperToolkit:
Wenn Sie es nuget bekommen, sollte es diese Zeilen in Ihre app.config oder web.config hinzufügen
<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>