繁体   English   中英

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

[英]Code-First Entity Framework w/ Stored Procedure returning results from complex Full-text Searches

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

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

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

我的问题是:

  1. 我的方法是否合情合理/遵循合理的最佳做法?
  2. 有没有其他人做过类似的经验教训?
  3. 有没有办法更有效地执行此操作(即将存储过程的结果直接返回/映射到实体而无需额外的查找?)

UPDATE

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

  1. 看起来你不能首先使用像实体框架代码这样的实体框架代码之类的SQL方法,而你的应用程序的其余部分可能会使用你,你可以“强迫”使用像你描述的存储过程来做某事。 这是不是最好的做法我不知道。 然而,它完成了工作,我不明白为什么它不明智。
  2. 是的 - 我已经并且仍在开发围绕EF codefirst的项目构建,我必须进行相当复杂的搜索,其中包括标记为“必须具有”的几个搜索参数以及标记为“很好”的几个值,并且从该返回加权结果。
  3. 根据结果​​集的复杂性,我认为您不需要对数据库进行第二次往返,我将向您展示我在下面执行此操作的方法。

请记住,以下只是一个例子:

    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;
    }

尽管storedprocedure将具有权重值的列,但在翻译时它将不会被映射。 如果需要,您可以从Person派生一个包含权重值的类。

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

获取@ Drauka(和谷歌)提供的一些见解就是我最初的迭代所做的。

  1. 创建存储过程以执行全文搜索。 即使支持,在EF中完成也非常复杂(作为一个例子,我的一些实体通过业务逻辑相关,我想将它们分组作为单个结果返回)。 存储过程映射到具有实体ID和Rank的DTO。
  2. 我修改了这个博主的代码片段/代码来调用存储过程,并填充我的DTO: http//www.lucbos.net/2012/03/calling-stored-procedure-with-entity.html
  3. 我使用存储过程的结果中的总计和分页信息填充结果对象,然后只加载当前结果页面的实体:

     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)); 

全面实施

由于这个问题得到了很多观点,我认为在发布我的最终解决方案的更多细节以供其他人帮助或可能的改进时可能是值得的。

完整的解决方案如下:

DatabaseExtensions类:

public static class DatabaseExtensions {
    public static IEnumerable<TResult> ExecuteStoredProcedure<TResult>(
             this Database database, 
             IStoredProcedure<TResult> procedure, 
             string spName) {
        var parameters = CreateSqlParametersFromProperties(procedure);
        var format = CreateSPCommand<TResult>(parameters, spName);
        return database.SqlQuery<TResult>(format, parameters.Cast<object>().ToArray());
    }

    private static List<SqlParameter> CreateSqlParametersFromProperties<TResult>
             (IStoredProcedure<TResult> procedure) {
        var procedureType = procedure.GetType();
        var propertiesOfProcedure = procedureType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        var parameters =
            propertiesOfProcedure.Select(propertyInfo => new SqlParameter(
                    string.Format("@{0}", 
                    (object) propertyInfo.Name), 
                    propertyInfo.GetValue(procedure, new object[] {})))
                .ToList();
        return parameters;
    }

    private static string CreateSPCommand<TResult>(List<SqlParameter> parameters, string spName)
    {
        var name = typeof(TResult).Name;
        string queryString = string.Format("{0}", spName);
        parameters.ForEach(x => queryString = string.Format("{0} {1},", queryString, x.ParameterName));

        return queryString.TrimEnd(',');
    }

    public interface IStoredProcedure<TResult> {
    }
}

用于保存存储过程输入的类:

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();

    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM