Linq 实体框架、代码优先和全文搜索

Linq 实体框架、代码优先和全文搜索,linq,entity-framework,full-text-search,ef-code-first,Linq,Entity Framework,Full Text Search,Ef Code First,我意识到有人问了很多关于全文搜索和实体框架的问题,但我希望这个问题有所不同 我使用实体框架,代码第一,需要做全文搜索。当我需要执行全文搜索时,我通常会有其他条件/限制,比如跳过前500行,或者过滤另一列,等等 我看到这是使用表值函数处理的-请参阅。这似乎是个正确的想法 不幸的是,直到EntityFramework5.0才支持表值函数(我相信,即使在那个时候,代码也不支持表值函数) 我真正的问题是,对于EntityFramework4.3和EntityFramework5.0,对于处理这个问题的最

我意识到有人问了很多关于全文搜索和实体框架的问题,但我希望这个问题有所不同

我使用实体框架,代码第一,需要做全文搜索。当我需要执行全文搜索时,我通常会有其他条件/限制,比如跳过前500行,或者过滤另一列,等等

我看到这是使用表值函数处理的-请参阅。这似乎是个正确的想法

不幸的是,直到EntityFramework5.0才支持表值函数(我相信,即使在那个时候,代码也不支持表值函数)

我真正的问题是,对于EntityFramework4.3和EntityFramework5.0,对于处理这个问题的最佳方法有什么建议。但具体来说:

  • 除了动态SQL(例如通过
    System.Data.Entity.DbSet.SqlQuery
    )之外,Entity Framework 4.3还有其他可用的选项吗

  • 如果我升级到EntityFramework5.0,有没有一种方法可以先在代码中使用表值函数

  • 谢谢,
    Eric

    我发现实现这一点的最简单方法是在SQL Server中设置和配置全文搜索,然后使用存储过程。将参数传递给SQL,允许DB执行其工作并返回复杂对象或将结果映射到实体。您不一定要有动态SQL,但它可能是最优的。例如,如果需要分页,则可以在每个请求上传入PageNumberPageSize,而不需要动态SQL。但是,如果每个查询的参数数量波动,这将是最佳解决方案。

    正如其他人提到的,我建议开始使用Lucene.NET

    Lucene有很高的学习曲线,但我找到了一个名为“”的包装器,可以在

    让我引用博客中的几个代码块来向您展示它的易用性。我刚开始使用它,但很快就掌握了窍门

    首先,从存储库中获取一些实体,或者在您的情况下,使用实体框架

    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"},
                };
            }
        }
    }
    
    现在,您有了一个可搜索的索引。剩下的唯一要做的就是…,搜索!您可以做一些非常令人惊奇的事情,但也可以像这样简单:(有关更多示例,请参阅博客或codeplex上的文档)

    var searcher=newdirectoryindexsearcher(
    新的DirectoryInfo(@“c:\index”),true);
    var查询=新术语查询(新术语(“名称”、“足球”));
    var searchService=newsearchservice();
    Func转换器=(文档)=>{
    返回新的ProductSearchResult{
    Id=int.Parse(doc.GetValues(“Id”)[0]),
    Name=doc.GetValues(“Name”)[0]
    };
    };
    IList results=searchService.SearchIndex(搜索器、查询、转换器);
    
    我最近有一个类似的要求,并最终编写了一个专门用于Microsoft全文索引访问的IQueryable扩展,可在此处使用

    使用EF6中引入的拦截器,您可以在linq中标记全文搜索,然后在dbcommand中替换它,如中所述:

    和EF地图

    public class NoteMap : EntityTypeConfiguration<Note>
    {
        public NoteMap()
        {
            // Primary Key
            HasKey(t => t.NoteId);
        }
    }
    
    这将生成类似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)   
    
    请注意,您应该使用局部变量,并且不能像这样在表达式中移动FTS包装器

    var q = db.Notes.Where(n => n.NoteText.Contains(FtsInterceptor.Fts("john")));
    
    这里的例子不是完整的解决方案。您需要了解全文搜索是如何工作的。假设您有一个搜索字段,用户键入2个单词以点击搜索。上面的代码将引发异常。您需要先对搜索短语进行预处理,然后使用逻辑AND或or将其传递给查询

    例如,您的搜索短语是“blah blah2”,那么您需要将其转换为:

    var searchTerm = @"\"blah\" AND/OR \"blah2\" "; 
    
    完整的解决办法是:

     value = Regex.Replace(value, @"\s+", " "); //replace multiplespaces
                        value = Regex.Replace(value, @"[^a-zA-Z0-9 -]", "").Trim();//remove non-alphanumeric characters and trim spaces
    
                        if (value.Any(Char.IsWhiteSpace))
                        {
                            value = PreProcessSearchKey(value);
                        }
    
    
     public static string PreProcessSearchKey(string searchKey)
        {
            var splitedKeyWords = searchKey.Split(null); //split from whitespaces
    
            // string[] addDoubleQuotes = new string[splitedKeyWords.Length];
    
            for (int j = 0; j < splitedKeyWords.Length; j++)
            {
                splitedKeyWords[j] = $"\"{splitedKeyWords[j]}\"";
            }
    
            return string.Join(" AND ", splitedKeyWords);
        }
    
    value=Regex.Replace(value,@“\s+”,“”)//替换多个空间
    value=Regex.Replace(value,@“[^a-zA-Z0-9-]”和“).Trim()//删除非字母数字字符并修剪空格
    if(value.Any(Char.IsWhiteSpace))
    {
    值=预处理搜索键(值);
    }
    公共静态字符串预处理searchKey(字符串searchKey)
    {
    var splitedKeyWords=searchKey.Split(null);//从空白处分割
    //string[]addDoubleQuotes=新字符串[splitedKeyWords.Length];
    对于(int j=0;j
    此方法使用AND逻辑运算符。您可以将其作为参数传递,并对and或or运算符使用该方法


    您必须不转义字母数字字符,否则当用户输入字母数字字符并且您没有服务器站点模型级验证时,它将引发异常

    至于问题(1),我相信这是你唯一的霍皮人建议使用Lucene.Net进行全文搜索。请看我的问题,并在EF的下一个版本中投票支持这些功能。我添加了示例NoteMap ClassThank@Ben,没有意识到EF可以以这种方式配置。这是一个不错的解决方案,但当输入字符串包含多个单词时,它会失败
    john doe
    搜索查询将导致
    全文搜索条件(john doe)
    中“doe”附近的语法错误,因为您正在使用
    进行模型创建
    我想这仅适用于
    代码优先
    方法。如何使其与
    数据库优先方法
    一起工作?@MaksimVi。您必须更改”
    包含
    中的
    将fulltextquery
    函数重写为“
    freetext
    ”(将
    Regex.Replace
    赋值为
    cmd.CommandText
    )。链接断开。您将其拿走了:(这里还提到:在这里找到它我在这里找到了一个类似的项目:有时我们忘记了我们总是可以依靠经过验证的存储过程!我还
    public class NoteMap : EntityTypeConfiguration<Note>
    {
        public NoteMap()
        {
            // Primary Key
            HasKey(t => t.NoteId);
        }
    }
    
    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());
        }
    }
    
    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();
            }
        }
    }
    
    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)   
    
    var q = db.Notes.Where(n => n.NoteText.Contains(FtsInterceptor.Fts("john")));
    
    var searchTerm = @"\"blah\" AND/OR \"blah2\" "; 
    
     value = Regex.Replace(value, @"\s+", " "); //replace multiplespaces
                        value = Regex.Replace(value, @"[^a-zA-Z0-9 -]", "").Trim();//remove non-alphanumeric characters and trim spaces
    
                        if (value.Any(Char.IsWhiteSpace))
                        {
                            value = PreProcessSearchKey(value);
                        }
    
    
     public static string PreProcessSearchKey(string searchKey)
        {
            var splitedKeyWords = searchKey.Split(null); //split from whitespaces
    
            // string[] addDoubleQuotes = new string[splitedKeyWords.Length];
    
            for (int j = 0; j < splitedKeyWords.Length; j++)
            {
                splitedKeyWords[j] = $"\"{splitedKeyWords[j]}\"";
            }
    
            return string.Join(" AND ", splitedKeyWords);
        }