6

Ho un sito Web ASP.NET MVC. Nel mio backend ho una tabella chiamata People con le seguenti colonne:qual è il modo migliore per memorizzare parametri di query filtrati dall'utente in una tabella di database?

  1. ID
  2. Nome
  3. Età
  4. Località
  5. ... (un certo numero di altri cols)

Ho una pagina Web generica che utilizza l'associazione modello per interrogare questi dati. Qui è la mia azione di controllo:

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

che mappa su qualcosa di simile:

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

Lo strato dataAccess controlla semplicemente ogni parametro per vedere se popolata da aggiungere alla db dove clausola. Funziona alla grande

Ora desidero essere in grado di memorizzare le query filtrate di un utente e sto cercando di capire il modo migliore per memorizzare un filtro specifico. Poiché alcuni filtri hanno solo un parametro nella queryString mentre altri hanno 10+ campi nel filtro, non riesco a capire il modo più elegante di archiviare questa query "informazioni sui filtri" nel mio database.

Opzioni mi vengono in mente sono:

  1. Avere una replica completa della tabella (con alcune cols in più), ma chiamarlo PeopleFilterQueries e compilare in ogni record un FilterName e mettere il valore del filtro nella ciascuno di campo (Nome, ecc.)

  2. Memorizza una tabella con solo FilterName e una stringa in cui memorizzo l'effettiva querystring Nome = Bill & Location = NewYork. In questo modo non dovrò continuare ad aggiungere nuove colonne se i filtri cambiano o aumentano.

Qual è la migliore pratica per questa situazione?

+0

Qual è lo scopo del filtro * dati *? Non * raccomanderei * di archiviare la "stringa di query effettiva" ma, ** se e solo se si tratta di un tipo di dati opaco ** (sul modello), allora memorizzerei un valore codificato che si associa bene a un oggetto dominio (e supporta serializzazione banale). Se è un tipo opaco - o meno - dipende dalle tue esigenze. –

+1

Hai mai bisogno di interrogare questi dati? La risposta a ciò ti guiderà a utilizzare la serializzazione o creare una tabella di query del filtro + tabella chiave/valore. – dotjoe

+0

Qual è il motivo per archiviarlo nel database? è per la segnalazione? Trending? Ricordando gli ultimi filtri? Con quale frequenza verranno utilizzate queste informazioni sul filtro? – ivowiblo

risposta

6

Se lo scopo è salvare un elenco di filtri utilizzati di recente, serializzo l'oggetto FilterParams completo in un campo/colonna XML dopo l'associazione del modello. Salvandolo in un campo XML, ti dai anche la flessibilità di utilizzare XQuery e DML in caso di necessità in un secondo momento per ulteriori query mirate sulle prestazioni delle informazioni.

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

E poi nella classe DataAccess si vorrà avere due metodi, uno per il salvataggio e uno per il recupero dei filtri:

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

Poi, quando si desidera recuperare un filtro salvato, forse da 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); 
     } 
} 

Ricordate che il vostro classe FilterParams deve avere un (cioè senza parametri) costruttore di default ed è possibile utilizzare l'attributo [XmlIgnore] per evitare p le proprietà possono essere serializzate nel database se lo si desidera.

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

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

Nota: SaveFilter restituisce Void e non vi è alcuna gestione degli errori per brevità.

+0

Questo è quello che faccio per salvare oggetti che non avranno MAI bisogno di essere segnalati o interrogati. Questa opzione renderà difficile vedere quanti utenti hanno una query salvata con nome = bob. E per "difficile" intendo solo che ci vorrà un sacco di tempo per eseguire la query. Nessun indice, ecc. –

+1

@DMoses Ben le prestazioni non erano direttamente parte dell'OP e non penso che si possa semplicemente dire che ci vorrà "molto tempo per eseguire una query" (e probabilmente downvote) senza doverlo supportare con alcune metriche. Esecuzione di un file 'SELECT XmlField.value ('(// SomePropertyName) [1]', 'PropertyType') come SomePropertyName FROM [TableName]' su una tabella con un campo XML contenente 13 oggetti di scena su 200k righe restituisce il valore analizzato dall'XML in meno di 2 secondi - non sono un fulmine sono d'accordo, ma per la flessibilità che si ottiene con il salvataggio di oggetti, penso che in alcuni casi il guadagno valga la pena. – Tr1stan

+0

@tristan Ho svalutato e ho pensato che la risposta fosse buona. Volevo solo che l'OP realizzasse un compromesso in termini di prestazioni. –

2

Anziché memorizzare la querystring, serializzo l'oggetto FilterParams come JSON/XML e memorizzo il risultato nel database.

Ecco un JSON Serializer che uso regolarmente:

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

È possibile quindi deserializzare l'oggetto e passarlo il codice di accesso ai dati secondo il vostro esempio di cui sopra.

0

Supponendo che un NoSQL/database ad oggetti come Berkeley DB è fuori questione, avrei sicuramente andare con l'opzione 1. Prima o poi troverete i seguenti requisiti o gli altri in arrivo:

  1. Consentire persone per salvare i loro filtri, etichettare, taggare, cercare e condividerli tramite segnalibri, tweets o altro.
  2. Modificare cosa significa un parametro o cosa fa, il che richiederà la versione dei filtri per la compatibilità con le versioni precedenti.
  3. Fornire funzioni di completamento automatico sui filtri, possibilmente utilizzando la cronologia dei filtri di un utente per informare il completamento automatico.

Quanto sopra sarà un po 'più difficile da soddisfare se si esegue qualsiasi tipo di serializzazione binaria/stringa in cui sarà necessario analizzare il risultato e quindi elaborarli.

Se si può utilizzare un NoSql DB, quindi si otterrà tutti i vantaggi di un negozio sql oltre a essere in grado di modellare il 'numero arbitrario di coppie chiave/valore' molto bene.

0

Hanno pensato di utilizzare i profili. Questo è un meccanismo integrato per memorizzare informazioni specifiche dell'utente. Dalla tua descrizione del tuo problema sembra essere in forma.

Profiles In ASP.NET 2.0

devo ammettere che l'attuazione M $ è un po 'datato, ma non v'è essenzialmente niente di sbagliato con l'approccio. Se si desidera eseguire il rollover, c'è un bel po 'di buon senso nella propria API.

+1

I profili in ASP.NET si adattano bene alle informazioni del profilo utente di base, tuttavia l'implementazione predefinita è difficile da eseguire su una semplice query. Guarda come memorizza i dati del profilo nel database, http://weblogs.asp.net/andrewrea/archive/2008/03/04/how-asp-net-profile-properties-are-serialized-in- the-database utilizzando-sql-profilo-provider.aspx –

2

Non hai menzionato lo scopo esatto della memorizzazione del filtro.

Se si desidera salvare il filtro in una tabella di database, avrei la seguente struttura della tabella.

  • FilterId
  • campo
  • fieldValue

Una tabella di esempio potrebbe essere

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

La risposta è molto più semplice di quanto si stanno rendendo:

Essentia È necessario memorizzare la query non elaborata nella propria tabella e correlarla alla tabella Persone. Non perdere tempo a memorizzare le singole opzioni di filtro.

Decidere su un valore da memorizzare (2 opzioni)

  1. Conservare l'URL Stringa query

    Questo id essere utile se vi piace applicazioni API open-style, e si desidera qualcosa che puoi passare piacevolmente avanti e indietro dal client al server e riutilizzare senza trasformazione.

  2. serializzare l'oggetto filtro come una stringa

    Questo è un approccio davvero bello se il vostro scopo per la memorizzazione di questi filtri rimane interamente lato server, e si vorrebbe mantenere i dati più vicino a un oggetto di classe.

di rapportare il vostro tavolo People al Query Filters Table:

La strategia migliore qui dipende da ciò che la vostra intenzione e esigenze di prestazioni sono. Alcuni suggerimenti qui sotto:

  • filtraggio semplice (ex. 2-3 filtri, 3-4 opzioni ciascuno)

    Usa Many-To-Many perché il numero di combinazioni suggerisce che le stesse combo filtro verranno utilizzati lotti di volte da molte persone.

  • filtraggio Complesso

    Usa One-To-Many come ci sono così tanti possibili singole query, è meno probabile che per essere riutilizzate spesso sufficiente a rendere l'extra-normalizzazione e le prestazioni colpito vale la pena.

Esistono sicuramente altre opzioni, ma dipenderanno da sfumature più dettagliate dell'applicazione. I suggerimenti di cui sopra funzionerebbero bene se si sta dicendo, cercando di tenere traccia delle "query recenti" per un utente, o delle opzioni di filtro "utente preferito" ...

opinione personale senza sapere molto di più sulla tua app, direi (1) memorizzare la stringa di query, e (2) utilizzare le tabelle relative OTM ... se e quando la vostra applicazione mostra la necessità di ulteriori prestazioni profilazione o problemi con i parametri di refactoring del filtro, quindi tornare indietro ... ma è probabile che lo faccia.

GL.

1

A mio parere il modo migliore per salvare il "Filtro" è quello di avere un qualche tipo di stringa di testo JSON con ciascuna delle "colonne nomi"

Così avrete qualcosa nel db come

Filtri tabella

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

Json fornirà molte funzionalità, come l'uso dell'età come array per avere più di un filtro per la stessa "colonna", ecc.

Anche json è una sorta di standard, quindi è possibile utilizzare questi "filtri" con altri db un giorno o semplicemente "visualizzare" il filtro o modificarlo in un modulo Web. Se salvi la Query, sarai collegato ad esso.

Bene, spero che sia d'aiuto!

Problemi correlati