2013-01-09 5 views
16

szukam porady projektowej dla następującego scenariusza:Code-First Entity Framework w/procedura przechowywana powracających wyników kompleksowego całego tekstu poszukiwane

Mam aplikacji kodu pierwszego EF5 MVC. Buduję funkcję wyszukiwania pełnotekstowego, która będzie zawierała wiele ważonych kolumn z wielu tabel. Ponieważ nie mogę utworzyć widoku z indeksem z tych tabel (niektóre z nich zawierają tekst/kolumny binarne), utworzyłem procedurę przechowywaną, która wyświetli ID mojego obiektu (np. PersonID) i pozycję związaną z tym obiektem na podstawie wyszukiwane hasła.

Moje obecne podejście polega na utworzeniu klasy pomocniczej do wykonywania wyszukiwania pełnotekstowego wywołującego procedurę (procedury) i załadowania wszystkich obiektów z kontekstu na podstawie zwróconych identyfikatorów.

Moje pytania są następujące:

  1. Czy moje podejście wydaje się rozsądne/follow rozsądny najlepsze praktyki?
  2. Czy ktoś inny zrobił coś podobnego z wyciągniętymi lekcjami?
  3. Czy istnieje sposób, aby to bardziej efektywnie (czyli mają wyniki procedury przechowywanej powrotnej/map bezpośrednio podmiotom bez dodatkowego look-up wymagane?)

aktualizacja

Przeniesiono moją szczegółową implementację z edycji pytania na własną odpowiedź, aby była bardziej zgodna z zalecaną często @ meta.stackexchange.com

Odpowiedz

11

Delegowanie to jako odpowiedź, a nie edytuje moje pytanie:

Biorąc niektóre wglądu dostarczonych przez @ Drauka (oraz Google) tutaj jest to, co zrobił dla mojej pierwszej iteracji.

  1. Utworzono procedurę składowaną, aby wykonać wyszukiwanie pełnotekstowe.To było naprawdę zbyt skomplikowane, aby je wykonać w EF, nawet jeśli jest obsługiwane (jako przykład niektóre z moich jednostek są powiązane za pomocą logiki biznesowej i chciałem je zgrupować wracając jako pojedynczy wynik). Procedura składowana mapuje do DTO z identyfikatorem podmiotu i Rangą.
  2. I zmodyfikowane tego blogera urywek/kod, aby wykonać połączenie do procedury przechowywanej i wypełnić moje dto: http://www.lucbos.net/2012/03/calling-stored-procedure-with-entity.html
  3. I wypełnić moje wyniki obiekt z sumy oraz informacji stronicowania z wyników procedury przechowywanej, a potem po prostu załadować podmioty na bieżącej stronie wyników:

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

Pełna Realizacji:

Ponieważ to pytanie otrzymuje wiele wyświetleń, pomyślałem, że warto byłoby opublikować więcej szczegółów na temat mojego ostatecznego rozwiązania, aby inni mogli pomóc lub poprawić sytuację.

Kompletne rozwiązanie wygląda następująco:

DatabaseExtensions klasa:

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

Klasa trzymać przechowywanych proc wejść:

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

Wyniki obiektu:

public class ResultsFTSDTO { 
    public int ID { get; set; } 
    public decimal weightRank { get; set; } 
} 

Na koniec wywołanie procedury przechowywanej:

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

    } 
13
  1. Nie można użyć metod SQL takich jak Najpierw przechowuj w kodzie entityframework, a reszta twojej aplikacji może być "zmuszona" do zrobienia czegoś z tak zapisanym procesem jak twój opis. Czy to najlepsza praktyka, nie wiem. Jednak robi to zadanie Nie rozumiem, dlaczego nie byłoby rozsądne.
  2. Tak - mam i wciąż pracuję nad projektem opartym na kodeksie EF EF, w którym musiałem przeprowadzić dość skomplikowane wyszukiwanie, które zawierało kilka parametrów wyszukiwania oznaczonych jako "must have" i kilka wartości oznaczonych jako "nice to have" oraz z tego zwraca ważony wynik.
  3. W zależności od złożoności zestawu wyników, nie wydaje mi się, że trzeba wykonać drugi cykl w bazie danych, a pokażę Wam sposób, w jaki robiłem to poniżej.

Należy pamiętać, że poniżej jest po prostu przykładem:

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

Choć przechowywana będzie mieć kolumnę wartości wagi nie dostanie odwzorowanym kiedy to przetłumaczyć. Możesz potencjalnie uzyskać klasę od Osoby, która zawiera wartość wagi, jeśli jej potrzebujesz.