2012-05-25 20 views
46

Come sviluppatore di ASP.Net abbastanza esperto che ha appena iniziato a utilizzare MVC, mi trovo a dover lottare un po 'per cambiare la mia mentalità da un tradizionale "controllo server ed event handler" modo di fare le cose, nel modo più dinamico di cose MVC. Penso che sto lentamente arrivando, ma a volte la MVC "magica" mi butta via.ASP.Net MVC e stato - come mantenere lo stato tra le richieste

Il mio attuale scenario è quello di creare una pagina web che permetta all'utente di cercare un file locale, caricarlo sul server e ripeterlo fino a quando non ha una lista di file con cui lavorare. Quando è contento dell'elenco file (che verrà visualizzato in una griglia sulla pagina), farà clic su un pulsante per elaborare i file ed estrarre alcuni dati che verranno archiviati in un database.

L'ultima parte non è così importante, in questo momento sono alle prese con qualcosa di così banale come creare un elenco di file e persistere nell'elenco tra le richieste. Nell'approccio tradizionale ciò sarebbe estremamente semplice: i dati verrebbero mantenuti in ViewState. Ma in MVC ho bisogno di passare i dati tra il controller e le viste e non capisco appieno come funzioni.

Credo di pubblicare meglio il mio tentativo, piuttosto incompleto, di codificarlo per spiegare il problema.

Al fine di mantenere i miei dati della lista di file, ho creato un ViewModel che è fondamentalmente un elenco dattiloscritto di file, insieme ad alcuni metadati in più:

public class ImportDataViewModel 
{ 
    public ImportDataViewModel() 
    { 
     Files = new List<ImportDataFile>(); 
    } 

    public List<ImportDataFile> Files { get; set; } 
... 

Nella vista, ho un modulo per la navigazione e caricare il file:

<form action="AddImportFile" method="post" enctype="multipart/form-data"> 
    <label for="file"> 
    Filename:</label> 
    <input type="file" name="file" id="file" /> 
    <input type="submit" /> 
    </form> 

la vista sta usando il ViewModel come il suo modello:

@model MHP.ViewModels.ImportDataViewModel 

Questo invierà il file per la mia azione:

public ActionResult AddImportFile(HttpPostedFileBase file, ImportDataViewModel importData) 
    { 

     if (file.ContentLength > 0) 
     { 
      ImportDataFile idFile = new ImportDataFile { File = file }; 
      importData.Files.Add(idFile); 
     } 

     return View("DataImport", importData); 
    } 

Questa azione restituisce la vista per la pagina dataimport insieme con l'istanza ViewModel che contiene l'elenco dei file.

Questo funziona bene fino a un certo punto, posso sfogliare un file e caricarlo, e posso vedere i dati viewmodel all'interno dell'azione, e poi anche se metto un punto di interruzione all'interno della vista e debug "this.Model ", va tutto bene.

Tuttavia, se provo a caricare un altro file, quando si inserisce un punto di interruzione all'interno dell'azione AddImportFile, il parametro importData è vuoto. Quindi la vista ovviamente non passa l'istanza attuale del suo modello all'azione.

Negli esempi MVC che ho attraversato, l'istanza del modello è passata "magicamente" al metodo action come parametro, quindi perché è vuota ora?

Suppongo che il vero problema sia la mia comprensione limitata di MVC e che probabilmente c'è una soluzione molto semplice a questo. Ad ogni modo, sarei estremamente grato se qualcuno potesse indicarmi la giusta direzione.

+0

Non si memorizzano i file caricati da nessuna parte sul server. Come ti aspetti che vengano mantenuti tra i postback? Tutto ciò che hai dentro il tuo '

' è un campo di input per un singolo file. Quindi tutto ciò che puoi aspettarti di ottenere nell'azione del controller è questo file. Niente di più. Non c'è magia. Non c'è ViewState in ASP.NET MVC. –

+0

Darin, grazie per il tuo commento. È corretto che non stia memorizzando il file sul server. Potrei memorizzarlo nel database, ma quello sarebbe uno spreco di spazio del database e la pazienza dell'utente, in quanto richiederebbe molto tempo e non è necessario. Quello che vorrei fare è solo di tenerlo in qualche modo nella memoria del server come variabile finché l'utente non decide di elaborarlo. Quindi memorizzerò l'output di tale elaborazione nel database. Penso che sto cercando di usare MVC nel modo sbagliato, o semplicemente non so come farlo. Non so ancora quale sia ... – TMan

+2

Tenere i file nella memoria del server è una delle cose peggiori. Evita questo a tutti i costi. Non solo questo consumerà molta memoria sul tuo server ma non dimenticherai che IIS potrebbe riciclare il dominio dell'applicazione in qualsiasi momento, perdendo tutti i dati archiviati in memoria. Inoltre, cosa succede se si esegue una server farm?Se si archivia nella memoria del server, ciò significa che solo 1 server della farm dispone di queste informazioni e se il bilanciamento del carico invia richieste successive su un altro server della farm, si perderanno le informazioni. –

risposta

46

E 'passato qualche tempo da quando ho postato la domanda, che era piuttosto colorata dalla mia piccola esperienza e conoscenza di MVC. Tuttavia ho ricevuto qualche input abbastanza utile, che alla fine mi ha portato a trovare una soluzione e anche a ottenere alcune informazioni su MVC.

Cosa mi ha buttato fuori, in primo luogo, era che si potrebbe avere un controller con un oggetto fortemente tipizzato come parametro, come questo:

public ActionResult DoSomething(MyClass myObject)... 

Questo oggetto ha avuto origine dallo stesso controller:

... 
return View(myObject); 
... 

Questo mi ha portato a credere che l'oggetto abbia vissuto attraverso questi due passaggi e che in qualche modo potessi aspettarmi che tu potessi inviarlo alla vista, fare qualcosa e poi "magicamente" riportarlo di nuovo al controller.

Dopo aver letto sul modello di rilegatura, ho capito che questo non è il caso. La vista è completamente morta e statica e, a meno che non si memorizzino le informazioni da qualche parte, non ci sono più.

Tornando al problema, che era la selezione e il caricamento di file dal client e la creazione di un elenco di questi file da visualizzare, mi sono reso conto che in generale ci sono tre modi per memorizzare le informazioni tra le richieste in MVC:

  1. è possibile memorizzare le informazioni nei campi modulo nella vista, e post-it al controllore in seguito
  2. si può persistere in un qualche tipo di stoccaggio, ad esempio, un file o un database
  3. È possibile memorizzarlo nella memoria del server accedendo agli oggetti che vivono in tutte le richieste, ad es. variabili di sessione

Nel mio caso, ho avuto fondamentalmente due tipi di informazioni a persistere: 1. i metadati del file (nome del file, dimensione del file ecc) 2. Il contenuto del file

Il "di L'approccio "the-book" sarebbe probabilmente quello di memorizzare i metadati nei campi modulo e il contenuto del file in un file o in db. Ma c'è anche un altro modo. Poiché so che i miei file sono piuttosto piccoli e ce ne saranno solo alcuni, e questa soluzione non verrà mai implementata in una server farm o simile, ho voluto esplorare l'opzione # 3 delle variabili di sessione. Anche i file non sono interessanti da perseguire oltre la sessione: vengono elaborati e scartati, quindi non volevo memorizzarli nel mio db.

Dopo aver letto questo ottimo articolo: Accessing ASP.NET Session Data Using Dynamics

ero convinto. Ho semplicemente creato una classe sessionbag come descritto in questo articolo, e poi ho potuto fare quanto segue in mio controller:

[HttpPost] 
    public ActionResult AddImportFile(HttpPostedFileBase file) 
    { 

     ImportDataViewModel importData = SessionBag.Current.ImportData; 
     if (importData == null) importData = new ImportDataViewModel(); 

     if (file == null) 
      return RedirectToAction("DataImport"); 

     if (file.ContentLength > 0) 
     { 
      ImportDataFile idFile = new ImportDataFile { File = file }; 
      importData.Files.Add(idFile); 
     } 

     SessionBag.Current.ImportData = importData; 

     return RedirectToAction("DataImport"); 
    } 

Sono pienamente consapevole che nella maggior parte dei casi, questa sarebbe una cattiva soluzione. Ma per i pochi KB di memoria server che i file occupano e con la semplicità di tutto questo, penso che abbia funzionato molto bene per me.

Il bonus aggiuntivo dell'utilizzo di SessionBag è che se l'utente ha immesso una voce di menu diversa e quindi è tornato, l'elenco dei file sarebbe ancora lì. Questo non sarebbe il caso, ad es. quando si scelgono i campi modulo/opzione di archiviazione file.

Come osservazione finale, mi rendo conto che la SessionBag è molto facile da abusare, data la semplicità di utilizzo. Ma se lo usi per quello che è destinato, cioè i dati di sessione, penso che possa essere uno strumento potente.

+0

Buon riassunto e spiegazione. – Lester

+1

e per quanto riguarda la sessione in scadenza? – garik

+0

@garik una soluzione per gestire le sessioni che vengono riciclate sta utilizzando un server di stato sessione o un server sql out-of-process secondo: https://technet.microsoft.com/en-us/library/cc754032(v=ws. 10) aspx –

10

Per quanto riguarda Caricamento

1) Forse considerare un uploader AJAX con l'HTML per consentire al all'utente di selezionare più file prima di essere inviati al server. Questo caricatore di file BlueImp Jquery AJAX è piuttosto sorprendente con una bella api: Blueimp Jquery File Upload.Permetterà agli utenti di trascinare e rilasciare o selezionare più file e modificare l'ordine dei file, includere/escludere ecc. Quindi, quando sono felici, possono premere il caricamento da inviare al controller o caricare il gestore per l'elaborazione sul lato server.

2) È possibile rendere persistente ogni caricamento sul database, anche se si ricaricherebbe l'intera pagina e si scriverà un modello di vista e un codice di rasoio aggiuntivi per ottenere l'effetto dell'elenco. Questo non è destinata probabilmente ad essere sensibile ...

Per quanto riguarda Mantenere WebForms Stato/MVC

il mantenimento dello stato tra le richieste è un po 'magia nera e voodoo. Entrando in ASP.NET MVC, approfondisci la comprensione del fatto che le applicazioni web comunicano utilizzando la richiesta e le risposte. Quindi vai ad abbracciare la rete come apolide e sviluppane da lì! Quando il tuo modello è pubblicato tramite il controller, è andato insieme a qualsiasi variabile nel controller! Tuttavia, prima che vada via, è possibile memorizzarne il contenuto in un database per recuperarlo in seguito.

Le applicazioni Web non possono mantenere lo stato reale come le applicazioni desktop. Ci sono molti modi in cui i framework ajax e alcuni strumenti di voodoo che le persone impiegano per simulare lo stato nell'ambiente HTTP. E una simulazione di stato è in realtà solo una falsa mimetizzazione dello stato. ASP.NET Web Forms tenta di simulare lo stato nel miglior modo possibile nascondendo la natura stateless di HTTP dallo sviluppatore. È possibile incontrare un sacco di mal di testa quando si cerca di utilizzare il proprio codice AJAX in tandem con il codice di markup Web Form e il proprio framework Ajax.

Sono davvero contento che si sta imparando MVC

Scherzi a parte, se si ottiene il/HTTP/mentalità di apolidia MVC, sarà molto facile da applicare i modelli di altri framework super-popolari come Ruby on Rails, SpringMVC (java), Django (python), CakePHP, ecc ... Questo semplice trasferimento di conoscenze ti aiuterà a diventare uno sviluppatore molto migliore e diventare VERAMENTE bravo in Ajax.

Sono davvero contento che tu stia imparando MVC 3, sono stato con alcuni stage in aziende molto grandi che avevano questi progetti di Web Forms pazzi di grandi dimensioni ASP.NET con il codice che volava ovunque solo per modificare alcuni numeri valori nel database (-_- ') Mi sentivo come se stavo usando le forbici per lavorare a maglia un bambino. Una semplice mossa sbagliata, e tutto si è rotto. Mi sentivo come lo sviluppo in PHP, vieni fuori sudando e non sono sicuro di cosa sia successo e su quale linea. Era quasi impossibile eseguire il debug e l'aggiornamento.

+0

anche se leggermente ovvio, vorrei aggiungere 2) a) sopra per utilizzare i campi nascosti per mantenere l'elenco dei file tra la richiesta. In definitiva, l'astrazione dello stato di visualizzazione nel modulo Web utilizza il campo nascosto, quindi non è una cattiva idea dopotutto e sarà più semplice da implementare rispetto all'opzione del database. – Kiran

+0

Max, grazie per una risposta approfondita. Per quanto riguarda 1) - Sì, probabilmente cercherò di implementare una funzionalità di upload di file multipli in una fase successiva per renderla più user friendly, ma non è davvero il problema principale. Penso che il problema principale qui sia più di stateful vs stateless. Mi sono imbattuto in questa discussione prima, ma non riesco a comprendere appieno l'intera idea di 'abbracciare la natura senza stato di MVC'. Stai dicendo che non dovremmo implementare lo stato nelle nostre app web? Non tutto dovrebbe essere memorizzato nel db. Prendi ad esempio il carrello della spesa. Gli esseri umani sono per natura dichiarativi, quindi perché non dovrebbero essere le web app? – TMan

+0

Kiran, penso che tu abbia ragione. Ora sto leggendo sul binding del modello, che sembra rappresentare una grande parte della "magia" percepita nel mio caso: in che modo i campi della forma nella vista vengono improvvisamente trasformati in istanze di classi fortemente tipizzate nel controller. Quando si cerca di applicare la "magia" in uno scenario diverso senza comprendere i principi sottostanti, ovviamente andrà male. Penso di essere sulla strada giusta ora. – TMan

Problemi correlati