6

Mam witrynę ASP.NET MVC. W moim backend Mam tabeli o nazwie People z następującymi kolumnami:jaki jest najlepszy sposób przechowywania paramerów zapytań filtrowanych przez użytkownika w tabeli bazy danych?

  1. ID
  2. Nazwa
  3. Wiek
  4. Położenie
  5. ... (szereg innych przełęcze) ​​

Mam ogólną stronę internetową, która używa wiązania modelu do kwerendy tych danych. Oto moje działania kontrolera:

public ActionResult GetData(FilterParams filterParams) 
{ 
     return View(_dataAccess.Retrieve(filterParams.Name, filterParams.Age, filterParams.location, . . .) 
} 

który odwzorowuje się na coś takiego:

http://www.mysite.com/MyController/GetData?Name=Bill .. . 

Warstwa DataAccess prostu sprawdza każdy parametr, aby sprawdzić, czy jego zaludnionych dodać do db gdzie klauzuli. Działa to świetnie.

Chcę teraz móc przechowywać przefiltrowane przez użytkownika zapytania i próbuję znaleźć najlepszy sposób na zapisanie określonego filtru. Ponieważ niektóre filtry mają tylko jeden parametr w queryString, podczas gdy inne mają ponad 10 pól w filtrze, nie mogę wymyślić najbardziej eleganckiego sposobu przechowywania tego zapytania "informacje o filtrze" w mojej bazie danych.

Opcje mogę myśleć to:

  1. mieć pełną kopią tabeli (z pewnymi dodatkowymi przełęcze), ale nazywają go PeopleFilterQueries i wypełnić w każdym rekordzie FILTERNAME i umieścić wartość filtra w każde pole (nazwa, itp.)

  2. Przechowywać tabelę z właśnie FilterName i ciągiem, w którym przechowuję rzeczywistą nazwę zapytania = Bill & Location = NewYork. W ten sposób nie będę musiał dodawać nowych kolumn, jeśli filtry będą się zmieniać lub rozwijać.

Jaka jest najlepsza praktyka w tej sytuacji?

+0

Jaki jest cel filtra * dane *? Nie polecałbym * przechowywania "rzeczywistego ciągu zapytania", ale ** jeśli i tylko wtedy, gdy jest to nieprzejrzysty typ danych ** (w modelu), wówczas zapisałbym zakodowaną wartość ładnie odwzorowującą obiekt domeny (i obsługuje trywialną serializację). Jeśli jest to rodzaj nieprzezroczysty - lub nie - zależy od Twoich wymagań. –

+1

Czy kiedykolwiek potrzebujesz zapytać o te dane? Odpowiedź na to poprowadzi Cię do korzystania z serializacji lub utworzenia tabeli zapytań filtru + tabela klucz/wartość. – dotjoe

+0

Jaki jest powód przechowywania go w bazie danych? jest na raportowanie? Trend? Pamiętasz ostatnie filtry? Jak często będą używane te informacje o filtrze? – ivowiblo

Odpowiedz

6

Jeśli celem jest zapisanie listy ostatnio używanych filtrów, serializowałbym cały obiekt FilterParams w polu/kolumnie XML po wystąpieniu powiązania modelu. Zapisując go w polu XML, dajesz sobie również swobodę korzystania z XQuery i DML, jeśli zajdzie taka potrzeba w późniejszym terminie, aby uzyskać więcej informacji o wynikach.

public ActionResult GetData(FilterParams filterParams) 
    { 
      // Peform action to get the information from your data access layer here 
      var someData = _dataAccess.Retrieve(filterParams.Name, filterParams.Age, filterParams.location, . . .); 

      // Save the search that was used to retrieve later here 
      _dataAccess.SaveFilter(filterParams); 
      return View(someData); 
    } 

A potem w swojej klasie DataAccess będziemy chcieli mieć dwie metody, jeden do zapisywania i jeden do pobierania filtry:

public void SaveFilter(FilterParams filterParams){ 
    var ser = new System.Xml.Serialization.XmlSerializer(typeof(FilterParams)); 
    using (var stream = new StringWriter()) 
      { 
       // serialise to the stream 
       ser.Serialize(stream, filterParams); 
      } 
    //Add new database entry here, with a serialised string created from the FilterParams obj 
    someDBClass.SaveFilterToDB(stream.ToString()); 
} 

Wtedy, gdy chcemy pobrać zapisany filtr, może przez ID:

public FilterParams GetFilter(int filterId){ 

     //Get the XML blob from your database as a string 
     string filter = someDBClass.GetFilterAsString(filterId); 

     var ser = new System.Xml.Serialization.XmlSerializer(typeof(FilterParams)); 

     using (var sr = new StringReader(filterParams)) 
     { 
      return (FilterParams)ser.Deserialize(sr); 
     } 
} 

Pamiętaj, że klasa FilterParams musi mieć domyślną (czyli bez parametrów) konstruktor, można użyć atrybutu [XmlIgnore] aby zapobiec p Jeśli chcesz, możesz je przekształcić do bazy danych.

public class FilterParams{ 
    public string Name {get;set;} 
    public string Age {get;set;} 

    [XmlIgnore] 
    public string PropertyYouDontWantToSerialise {get;set;} 
} 

Uwaga: SaveFilter zwraca Void i nie ma obsługi błędów dla zwięzłości.

+0

To jest to, co robię dla zapisywania obiektów, które NIGDY nie będą musiały być zgłaszane lub pytane. Ta opcja utrudni sprawdzenie, ilu użytkowników ma zapisane zapytanie o nazwie = bob. I przez "trudne" mam na myśli tylko tyle, że wykonanie zapytania zajmie dużo czasu. Brak indeksów itp. –

+1

@DMoses Dobra wydajność nie była bezpośrednio częścią OP i nie sądzę, że możesz po prostu powiedzieć, że zajmie to "dużo czasu, aby uruchomić zapytanie" (i prawdopodobnie w dół) bez tworzenia kopii zapasowej z pewnymi wskaźnikami. Uruchamianie 'SELECT XmlField.value ('(// SomePropertyName) [1]', 'PropertyType') jako SomePropertyName od [TableName]' w tabeli z polem XML zawierającym 13 rekwizytów w ponad 200k wierszy zwraca wartość przeanalizowaną z XML w mniej niż 2 sekundy - nie piorun Zgadzam się, ale dla elastyczności jaką daje zapisywanie obiektów, myślę, że wypłata jest tego warta w niektórych przypadkach. – Tr1stan

+0

@tristan Podniosłem głos i uznałem, że twoja odpowiedź była dobra. Chciałem tylko, aby OP zorientował się, że istnieje kompromis w zakresie wydajności. –

2

Zamiast przechowywać kwerendy, serializuje się obiekt FilterParams jako JSON/XML i zapisuję wynik w bazie danych.

Oto JSON serializer ja regularnie używać:

using System.IO; 
using System.Runtime.Serialization.Json; 
using System.Text; 

namespace Fabrik.Abstractions.Serialization 
{ 
    public class JsonSerializer : ISerializer<string> 
    { 
     public string Serialize<TObject>(TObject @object) { 
      var dc = new DataContractJsonSerializer(typeof(TObject)); 
      using (var ms = new MemoryStream()) 
      { 
       dc.WriteObject(ms, @object); 
       return Encoding.UTF8.GetString(ms.ToArray()); 
      } 
     } 

     public TObject Deserialize<TObject>(string serialized) { 
      var dc = new DataContractJsonSerializer(typeof(TObject)); 
      using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(serialized))) 
      { 
       return (TObject)dc.ReadObject(ms); 
      } 
     } 
    } 
} 

Następnie można deserializować obiekt i przekazać mu swój kod dostępu do danych, jak na swoim przykładzie powyżej.

0

Zakładając, że NoSQL/database przedmiot, taki jak Berkeley DB to nie wchodzi w rachubę, na pewno go z opcją 1. Wcześniej czy później przekonasz się następujące wymagania lub innym wymyślanie:

  1. Pozostawić ludzie zapisują filtry, etykiety, znaczniki, wyszukiwania i udostępniają je za pośrednictwem zakładek, tweetów lub cokolwiek innego.
  2. Zmienić, co parametr oznacza lub co robi, co będzie wymagało zmiany wersji filtrów pod kątem kompatybilności wstecznej.
  3. Zapewnij funkcje autouzupełniania dla filtrów, korzystając z historii filtra użytkownika, aby poinformować o autouzupełnieniu.

Powyższe będzie nieco trudniejsze do spełnienia, jeśli wykonasz jakąkolwiek serializację binarną/ciągową, w której musisz przeanalizować wynik, a następnie przetworzyć go.

Jeśli może użyć NoSql DB, to dostaniesz wszystkie zalety sklepu sql i będziesz w stanie bardzo dobrze modelować "dowolną liczbę par klucz/wartość".

0

Pomyśleli Państwo o używaniu profili. Jest to wbudowany mechanizm służący do przechowywania informacji o użytkowniku. Z twojego opisu problemu wynika, że ​​pasuje.

Profiles In ASP.NET 2.0

muszę przyznać, że M $ realizacji jest nieco stary, ale nie ma w zasadzie nic złego podejścia. Jeśli chciałbyś rzucić własną, w swoim API jest całkiem sporo dobrego.

+1

Profile w środowisku ASP.NET są dobrze dostosowane do bardzo podstawowych informacji o profilu użytkownika, jednak domyślna implementacja jest trudna do wykonania prostego zapytania. Zobacz, jak przechowuje dane profilu w bazie danych, http://weblogs.asp.net/andrewrea/archive/2008/03/04/how-asp-net-profile-properties-are-serialized-in- the-database-using-sql-profile-provider.aspx –

2

Nie wspomniałeś o dokładnym celu przechowywania filtra.

Jeśli nalegasz, aby zapisać filtr w tabeli bazy danych, będę miał następującą strukturę tabeli.

  • FilterId
  • Pole
  • FieldValue

Przykładem stół może być

FilterId Field FieldValue 
1  Name  Tom 
1  Age  24 
1  Location IL  
3  Name  Mike 
... 
2

Odpowiedź jest znacznie bardziej proste niż robisz to:

Essentia lly powinieneś przechowywać surowe zapytanie w swojej własnej tabeli i powiązać je z tabelą People. Nie przejmuj się przechowywaniem poszczególnych opcji filtrów.

Zdecyduj się na wartości do przechowywania (2 opcje)

  1. Store URL Query String

    Ten id być korzystne, jeśli lubisz otwartych aplikacji API stylu i chcesz coś, co można ładnie przekazać od klienta do serwera i ponownie użyć bez transformacji.

  2. serializacji obiektu filtra w postaci ciągu

    Jest to naprawdę miłe podejście jeśli celem przechowywania tych filtrów pozostaje całkowicie po stronie serwera, a chcesz zachować dane bliżej do obiektu klasy.

skojarz tabelę People do swojej Query Filters Table:

Najlepszą strategią tutaj zależy od tego, co twój zamiar i potrzeby osiągów. Niektóre propozycje poniżej:

  • prostego filtrowania (ex. 2-3, 3-4 Opcje filtrów każda)

    Zastosowanie Many-To-Many bo liczba kombinacji sugeruje, że te same kombinacje filtrów zostaną wykorzystane losy razy przez wiele osób.

  • Complex filtrowanie

    Zastosowanie One-To-Many jak istnieje tak wiele możliwych indywidualnych zapytań, to mniej prawdopodobne są do ponownego wykorzystania na tyle często, aby dogrywki normalizacji i wydajność hit warto poświęcić chwilę.

Z pewnością istnieją inne opcje, ale zależą one od bardziej szczegółowych niuansów aplikacji. Powyższe sugestie sprawdziłyby się dobrze, jeśli powiesz, próbując śledzić "ostatnie zapytania" dla użytkownika lub "ulubione opcje użytkownika" opcje filtrowania ...

Osobista opinia Nie wiedząc o wiele więcej aplikacji, powiedziałbym (1) przechowywać ciąg kwerendy, oraz (2) używać OTM związanych tabel ... jeśli i kiedy aplikacja wskazuje na potrzebę dalszego wykonywania profilowanie lub problemy z parametrami filtra refaktoryzacji, a następnie wracają ... ale są szanse, że tak się nie stanie.

GL.

1

moim zdaniem najlepszym sposobem, aby uratować „Filter” ma mieć jakąś json ciąg tekstowy z każdym z „kolumny nazwami”

więc trzeba będzie coś w db jak

Filtry tabeli

FilterId = 5; FilterParams = {'age': '> 18', ...

Json zapewni wiele możliwości, takich jak użycie wieku jako tablicy, aby mieć więcej niż jeden filtr do tej samej "kolumny", itp.

Również json jest pewnego rodzaju standardem, więc możesz użyć tego "filtru" z innym środowiskiem db lub po prostu "wyświetlić" filtr lub edytować go w formularzu internetowym. Jeśli zapiszesz zapytanie, zostaniesz do niego dołączony.

Cóż, mam nadzieję, że to pomoże!

Powiązane problemy