C# 代码优先实体框架,带有从复杂全文搜索返回结果的存储过程

C# 代码优先实体框架,带有从复杂全文搜索返回结果的存储过程,c#,entity-framework,stored-procedures,full-text-search,C#,Entity Framework,Stored Procedures,Full Text Search,我正在寻找以下场景的设计建议: 我有一个代码优先的EF5 MVC应用程序。我正在构建一个全文搜索功能,它将包含来自许多表的多个加权列。由于我无法使用这些表中的索引创建视图(其中一些表包含文本/二进制列),因此我创建了一个存储过程,该过程将根据搜索词输出我的对象ID(例如PersonID)和与该对象关联的排名 我当前的方法是创建一个帮助器类来执行全文搜索,全文搜索调用存储过程,并根据返回的ID从上下文加载所有对象 我的问题是: 我的方法是否合理/遵循合理的最佳实践 还有其他人做过类似的事情吗 是否

我正在寻找以下场景的设计建议:

我有一个代码优先的EF5 MVC应用程序。我正在构建一个全文搜索功能,它将包含来自许多表的多个加权列。由于我无法使用这些表中的索引创建视图(其中一些表包含文本/二进制列),因此我创建了一个存储过程,该过程将根据搜索词输出我的对象ID(例如
PersonID
)和与该对象关联的排名

我当前的方法是创建一个帮助器类来执行全文搜索,全文搜索调用存储过程,并根据返回的ID从上下文加载所有对象

我的问题是:

  • 我的方法是否合理/遵循合理的最佳实践
  • 还有其他人做过类似的事情吗
  • 是否有一种方法可以更有效地执行此操作(即,让存储过程的结果直接返回/映射到实体,而不需要额外的查找?)
  • 更新

    将我的详细实现从问题编辑移到自己的答案中,以便更符合经常推荐的@meta.stackexchange.com

  • 鉴于不能先将containstable这样的SQL方法与entityframework代码结合使用,而应用程序的其余部分可能会使用这些方法,因此可能会“被迫”使用类似于您描述的StoredProcess。我不知道这是否是最佳实践。不管它如何完成任务,我不明白为什么它不明智
  • 是的-我已经并且仍然在围绕EF codefirst进行项目构建,我必须进行相当复杂的搜索,其中包括几个标记为“必须拥有”的搜索参数和几个标记为“很好拥有”的值,并从中返回加权结果
  • 根据结果集的复杂程度,我认为您不需要进行第二次数据库往返,我将在下面向您展示我一直在使用的方法
  • 请记住,以下只是一个示例:

        public List<Person> GetPeople(params string[] p)
        {
            var people = new List<Person>();
    
            using (var db = new DataContext())
            {
                var context = ((IObjectContextAdapter)db).ObjectContext;
    
                db.Database.Connection.Open();
    
                var command = db.Database.Connection.CreateCommand();
                command.CommandText = "SomeStoredProcedureReturningWeightedResultSetOfPeople";
                command.CommandType = System.Data.CommandType.StoredProcedure;
    
                //Add parameters to command object
    
                people = context.Translate<Person>(command.ExecuteReader()).ToList();
            }
    
            return people;
        }
    
    public List GetPeople(参数字符串[]p)
    {
    var people=新列表();
    使用(var db=new DataContext())
    {
    var context=((IObjectContextAdapter)db).ObjectContext;
    db.Database.Connection.Open();
    var command=db.Database.Connection.CreateCommand();
    command.CommandText=“SomeStoredProcedureReturningWeightedResultSetOfPeople”;
    command.CommandType=System.Data.CommandType.StoredProcess;
    //向命令对象添加参数
    people=context.Translate(command.ExecuteReader()).ToList();
    }
    还人,;
    }
    
    即使storedprocedure将有一列作为权重值,但在转换它时它也不会被映射。
    如果需要,您可以从Person派生一个包含权重值的类。

    将此作为答案而不是对我的问题进行编辑:

    从@Drauka(和谷歌)提供的一些见解来看,这就是我在初始迭代中所做的

  • 创建存储过程以执行全文搜索。这真的太复杂了,即使得到支持,也无法在EF中完成(例如,我的一些实体通过业务逻辑进行关联,我希望将它们分组,作为单个结果返回)。存储过程映射到具有实体id和列组的DTO
  • 我修改了这个blogger的代码片段/代码以调用存储过程,并填充我的DTO:
  • 我使用存储过程结果中的总计和分页信息填充结果对象,然后只加载当前结果页面的实体:

    int[] projectIDs = new int[Settings.Default.ResultsPerPage];
    foreach (ProjectFTS_DTO dto in 
              RankedSearchResults
              .Skip(Settings.Default.ResultsPerPage * (pageNum - 1))
              .Take(Settings.Default.ResultsPerPage)) {
                 projectIDs[index] = dto.ProjectID;
                 index++;
            }
    
    IEnumerable<Project> projects = _repository.Projects
                .Where(o=>projectIDs.Contains(o.ProjectID));
    
    最后调用存储过程:

    public List<ResultsFTSDTO> getAdvancedFTSResults(
                string searchText, int minRank,
                bool IncludeTitle,
                bool IncludeDescription,
                int StartYear,
                int EndYear,
                string FilterTags) {
    
            AdvancedFTS sp = new AdvancedFTS() {
                SearchText = searchText,
                MinRank = minRank,
                IncludeTitle=IncludeTitle,
                IncludeDescription=IncludeDescription,
                StartYear=StartYear,
                EndYear = EndYear,
                FilterTags=FilterTags
            };
            IEnumerable<ResultsFTSDTO> resultSet = _context.Database.ExecuteStoredProcedure(sp, "ResultsAdvancedFTS");
            return resultSet.ToList();
    
        }
    
    公共列表getAdvancedFTSResults(
    字符串searchText,int-minRank,
    布尔包括迪特尔,
    bool包括描述,
    国际StartYear,
    年底,,
    字符串过滤器(标签){
    AdvancedFTS sp=新的AdvancedFTS(){
    SearchText=SearchText,
    MinRank=MinRank,
    IncludeTitle=IncludeTitle,
    IncludeDescription=IncludeDescription,
    StartYear=StartYear,
    年终=年终,
    过滤器标签=过滤器标签
    };
    IEnumerable resultSet=_context.Database.ExecuteStoredProcedure(sp,“ResultsAdvancedFTS”);
    返回resultSet.ToList();
    }
    
    class AdvancedFTS : 
             DatabaseExtensions.IStoredProcedure<AdvancedFTSDTO> {
        public string SearchText { get; set; }
        public int MinRank { get; set; }
        public bool IncludeTitle { get; set; }
        public bool IncludeDescription { get; set; }
        public int StartYear { get; set; }
        public int EndYear { get; set; }
        public string FilterTags { get; set; }
    }
    
    public class ResultsFTSDTO {
        public int ID { get; set; }
        public decimal weightRank { get; set; }
    }
    
    public List<ResultsFTSDTO> getAdvancedFTSResults(
                string searchText, int minRank,
                bool IncludeTitle,
                bool IncludeDescription,
                int StartYear,
                int EndYear,
                string FilterTags) {
    
            AdvancedFTS sp = new AdvancedFTS() {
                SearchText = searchText,
                MinRank = minRank,
                IncludeTitle=IncludeTitle,
                IncludeDescription=IncludeDescription,
                StartYear=StartYear,
                EndYear = EndYear,
                FilterTags=FilterTags
            };
            IEnumerable<ResultsFTSDTO> resultSet = _context.Database.ExecuteStoredProcedure(sp, "ResultsAdvancedFTS");
            return resultSet.ToList();
    
        }