Eri*_*ric 52 linq full-text-search entity-framework ef-code-first
我意识到有很多关于全文搜索和实体框架的问题,但我希望这个问题有点不同.
我正在使用Entity Framework,Code First,需要进行全文搜索.当我需要执行全文搜索时,我通常还会有其他条件/限制 - 例如跳过前500行,或过滤其他列等.
我看到这是使用表值函数处理的 - 请参阅http://sqlblogcasts.com/blogs/simons/archive/2008/12/18/LINQ-to-SQL---Enabling-Fulltext-searching.aspx.这似乎是正确的想法.
不幸的是,在实体框架5.0之前不支持表值函数(我认为,即使这样,它们也不支持Code First).
我真正的问题是,对于实体框架4.3和实体框架5.0,最佳处理方式的建议是什么.但具体来说:
除了动态SQL(System.Data.Entity.DbSet.SqlQuery例如,通过),Entity Framework 4.3是否有可用的选项?
如果我升级到Entity Framework 5.0,有没有办法可以首先使用表值函数和代码?
谢谢,埃里克
Ben*_*Ben 51
使用EF6中引入的拦截器,您可以在linq中标记全文搜索,然后在dbcommand中将其替换为http://www.entityframework.info/Home/FullTextSearch中所述:
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;
}
}
}
}
}
static class LanguageExtensions
{
public static bool In<T>(this T source, params T[] list)
{
return (list as IList<T>).Contains(source);
}
}
Run Code Online (Sandbox Code Playgroud)
例如,如果您有带有FTS索引字段的Note Note NoteText:
public class Note
{
public int NoteId { get; set; }
public string NoteText { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
和EF地图
public class NoteMap : EntityTypeConfiguration<Note>
{
public NoteMap()
{
// Primary Key
HasKey(t => t.NoteId);
}
}
Run Code Online (Sandbox Code Playgroud)
和它的背景:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用非常简单的FTS查询语法:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这将生成SQL之类的
exec sp_executesql N'SELECT TOP (10)
[Extent1].[NoteId] AS [NoteId],
[Extent1].[NoteText] AS [NoteText]
FROM [NS].[NOTES] AS [Extent1]
WHERE contains([Extent1].[NoteText], @p__linq__0)',N'@p__linq__0 char(4096)',@p__linq__0='(john)
Run Code Online (Sandbox Code Playgroud)
请注意你应该使用局部变量并且不能在表达式中移动FTS包装器
var q = db.Notes.Where(n => n.NoteText.Contains(FtsInterceptor.Fts("john")));
Run Code Online (Sandbox Code Playgroud)
Mat*_*att 17
我发现实现这一点的最简单方法是在SQL Server中设置和配置全文搜索,然后使用存储过程.将您的参数传递给SQL,允许DB完成其工作并返回复杂对象或将结果映射到实体.您不一定必须拥有动态SQL,但它可能是最佳的.例如,如果需要分页,则可以在每个请求中传递PageNumber和PageSize,而无需动态SQL.但是,如果每个查询的参数数量波动,那么它将是最佳解决方案.
正如其他人提到的,我会说开始使用 Lucene.NET
Lucene 的学习曲线非常高,但我找到了一个名为“ SimpleLucene ”的包装器,可以在CodePlex上找到
让我引用博客中的几个代码块,向您展示它的易用性。我刚刚开始使用它,但很快就掌握了它。
首先,从您的存储库中获取一些实体,或者在您的情况下,使用实体框架
public class Repository
{
public IList<Product> Products {
get {
return new List<Product> {
new Product { Id = 1, Name = "Football" },
new Product { Id = 2, Name = "Coffee Cup"},
new Product { Id = 3, Name = "Nike Trainers"},
new Product { Id = 4, Name = "Apple iPod Nano"},
new Product { Id = 5, Name = "Asus eeePC"},
};
}
}
}
Run Code Online (Sandbox Code Playgroud)
您要做的下一件事是创建索引定义
public class ProductIndexDefinition : IIndexDefinition<Product> {
public Document Convert(Product p) {
var document = new Document();
document.Add(new Field("id", p.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
document.Add(new Field("name", p.Name, Field.Store.YES, Field.Index.ANALYZED));
return document;
}
public Term GetIndex(Product p) {
return new Term("id", p.Id.ToString());
}
}
Run Code Online (Sandbox Code Playgroud)
并为其创建搜索索引。
var writer = new DirectoryIndexWriter(
new DirectoryInfo(@"c:\index"), true);
var service = new IndexService();
service.IndexEntities(writer, Repository().Products, ProductIndexDefinition());
Run Code Online (Sandbox Code Playgroud)
所以,您现在有了一个可搜索的索引。剩下唯一要做的就是..,搜索!你可以做非常了不起的事情,但也可以像这样简单:(更多示例请参阅博客或关于 codeplex 的文档)
var searcher = new DirectoryIndexSearcher(
new DirectoryInfo(@"c:\index"), true);
var query = new TermQuery(new Term("name", "Football"));
var searchService = new SearchService();
Func<Document, ProductSearchResult> converter = (doc) => {
return new ProductSearchResult {
Id = int.Parse(doc.GetValues("id")[0]),
Name = doc.GetValues("name")[0]
};
};
IList<Product> results = searchService.SearchIndex(searcher, query, converter);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
29367 次 |
| 最近记录: |