簡體   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