2009-11-26 21 views
15

Mi chiedevo solo come si stavano avvicinando le persone a questa situazione. È qualcosa che sembra un punto debole nel mio utilizzo di MVC con ORM (NHibernate in questo caso) ...ASP.NET MVC - Aggiornamento parziale del modello dalla vista

Supponiamo di avere un'entità complessa e complessa nel modello. Probabilmente avrai una pagina di amministrazione per gestire oggetti di questo tipo. Se l'entità è complicata, è improbabile che tu modifichi l'intera entità in un unico modulo. È ancora necessario passare le proprietà pertinenti alla vista e incorporare le modifiche a tali proprietà nel modello quando la vista le restituisce.

Cosa fa qualcuno in questa situazione?

  • Creare un modello di vista che sia (o contenga) un sottoinsieme delle proprietà delle entità. Passa a questa e alla vista. Nel metodo di azione 'modifica' nel controller, prelevare l'oggetto dal repository, eseguire tutte le proprietà in ViewModel e applicarle all'oggetto Model (model.a = viewmodel.a, modelb = viewmodel.b). Questo sembra l'ovvia strada sensata, ma genera un sacco di noioso codice idraulico. Anche questo complica un po 'la convalida.

  • Qualcos'altro?

Ho guardato brevemente all'automapper - ma questo non sembra adattarsi esattamente al conto, forse mi sbaglio?

Grazie.

+0

io uso un modello di vista e si sono completamente a destra lo fa portare a qualche mano sinistra codice mano destra noioso. Da ciò che ho raccolto AutoMapper allevia parte di quel codice. – Aaron

+0

Sì, ho la sensazione che sia così che deve essere. Non si sa mai, qualcuno potrebbe avere qualche trucco ingegnoso però ... – UpTheCreek

risposta

11

Questo suona come lo scenario perfetto per l'automapper. Si crea una classe del modello di vista che contiene un sottoinsieme dei campi o il modello reale e si lascia che AutoMapper si preoccupi di estrarre i valori dall'oggetto del modello di dominio nell'oggetto del modello di vista. Che problemi hai con questo approccio?

Considerate questo esempio:

Ecco il vostro modello di dominio e il vostro modello di vista

public class Person 
{ 
    public string FirstName 
    { get; set; } 

    public string LastName 
    { get; set; } 

    public string HomeNumber 
    { get; set; } 

    public string Address1 
    { get; set; } 

    public string Address2 
    { get; set; } 
} 

public class PersonViewModel 
{ 
    public string FirstName 
    { get; set; } 

    public string LastName 
    { get; set; } 

    public string HomeNumber 
    { get; set; } 
} 

Ecco la mappatura, è necessario creare una mappatura in entrambe le direzioni da dm-> vm e vm-> dm.

Da quello che ho visto usando Automapper è che se si esegue la mappatura dall'oggetto A a B e B ha una proprietà che A non ha, verrà ripristinata. Così, quando creo la mappa, la dirigo per ignorare quelle proprietà mancanti. Non sono un esperto di Automapper quindi potrei utilizzarlo in modo errato.

Mapping

Mapper.CreateMap<Person, PersonViewModel>(); 
// Automapper will reset values in dest which don't exist in source, so be sure to ignore them! 
Mapper.CreateMap<PersonViewModel, Person>() 
    .ForMember(dest => dest.HomeNumber, opt => opt.Ignore()); 

Infine utilizzo:

Person p = new Person() 
{ 
    FirstName = "First", 
    LastName = "Last", 
    Address1 = "add 1", 
    Address2 = "add 2" 
}; 

PersonViewModel pvm = Mapper.Map<Person, PersonViewModel>(p); 
// Map to a new person 
Person p2 = Mapper.Map<PersonViewModel, Person>(pvm); 

// Map to the existing person just to update it 
Person p3 = new Person() 
{ 
    HomeNumber = "numberHere" 
}; 

// This will update p3 
Mapper.Map<PersonViewModel, Person>(pvm, p3); 

causa dell'esclusione, questo è ovviamente meno ideale, ma molto meglio che fare manualmente il tutto.

+0

Grazie Sayed, questo va bene per le situazioni di "sola vista". Ma, a quanto ho capito, automapper non è in grado di eseguire il mapping all'oggetto del modello di dominio fornito dai valori modificati dalla vista (ad esempio in una situazione di modulo di amministrazione "modifica"). Forse mi sbaglio? – UpTheCreek

+0

Ho appena aggiunto un esempio per te. –

+0

Grazie per gli esempi dettagliati, sembra che ci sia di più per l'automapper di quanto non avessi realizzato. Farò un po 'più di ricerca. – UpTheCreek

1

Cosa succede se si dispone di un modello completo ma ogni pagina utilizza e aggiorna solo la parte richiesta? Quindi si aggiorna il modello aziendale utilizzando i dati di visualizzazione completi nell'ultima pagina.

+0

Grazie. Penso che tu stia suggerendo una sorta di forma di stile "mago" a più pagine? Non sto parlando di situazioni in cui devo aggiornare l'intero modello in blocchi più piccoli, ma piuttosto solo i dettagli del passaggio di un modello per l'aggiornamento. – UpTheCreek

+0

No, non credo che lo sia. Penso che stia suggerendo di non usare un viewmodel. Basta passare il modello completo e fare in modo che l'azione cambi solo/aggiorni le proprietà necessarie. Semplificare il processo rimuovendo l'impianto idraulico supplementare dell'oggetto VM. –

+0

Quindi, per quanto riguarda il bit 'aggiorna il modello di business usando * complete view data at last page *' then? – UpTheCreek

1

y, viewmodel + automapper.

ps. un modello complicato, tende a ... complicare tutto - ne vale davvero la pena nel tuo scenario?

2

Perché non utilizzare TryUpdateModel con la raccolta moduli.

Se la vista sta modificando una persona

public class Person 
{ 
    public string ID { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Email { get; set; } 
    public string Address { get; set; } 
} 

E il tuo punto di vista è solo la modifica nome e cognome, si può fare questo:

public ActionResult Action(FormCollection form) 
{ 
    Person personToUpdate = Repository.GetPerson(form["ID"]); 

    TryUpdateModel<Person>(personToUpdate, form); 

    Repository.Update(personToUpdate) 

    return View(); 
} 

che aggiornerà solo persona con gli articoli quella parte della raccolta del modulo. Se non si desidera aggiornare un campo, non inviarlo con il modulo.

+0

Parlo senza certezza e mi piacerebbe avere qualche feedback. Penso che tu non voglia farlo perché 1. È scritto meno fortemente, 2. Non può essere combinato con un raccoglitore di modelli personalizzato. Levi ha spiegato da qualche parte che updatemodel era destinato solo all'aggiornamento del viewmodel. – Martin

0

In questo momento sto lavorando su un progetto di grandi dimensioni utilizzando S # arp Architettura e im anche utilizzando l'approccio:

Model -> ViewModel -> Model 

uso il ViewModel per la parte di rilegatura e di convalide, l'altro approccio è quello di utilizzare il Model Directly (con tryUpdateModel/UpdateModel che abbiamo usato durante lo sviluppo del prototipo) ma per scenari complessi finiamo per gestire situazioni speciali come SelectLists/Checkbox/Projections/HMAC Validations in un piccolo ViewModel e usando un sacco di Request.Form [" key "] = (, l'altro svantaggio sta gestendo le situazioni di errore in cui si desidera ripopolare il modulo con l'input dell'utente, l'ho trovato un po 'più complicato usando direttamente il Model (usando un ViewModel w Prendiamo molto vantaggio dal valore tentato di ModelState, salvandoci un paio di viaggi nel DB, chiunque abbia affrontato questo scenario saprà cosa intendo).

Questo approccio richiede un po 'di tempo, proprio come hai detto tu, finisci per abbinare le proprietà, ma secondo me è la strada da percorrere per forme complesse.

Vale la pena ricordare che utilizziamo semplicemente ViewModels per gli scenari Crea/Modifica, per quasi tutto il resto viene utilizzato direttamente il modello.

Non ho ancora utilizzato gli autommapers, ma sicuramente ci proverò.

1

Io uso un approccio simile al tuo (nel mio caso Entity Framework) con Entity -> ViewModel -> View ma solo su viste con entità "complesse" che hanno relazioni 1: M o M: M. Nella maggior parte dei casi ho preso la strada bassa e sono andato a Entity-> View quando ho una semplice entità.

My ViewModel è definito come Entity + proprietà di supporto: SelectList o MultiSelectList e uno string o List<string>. Userò anche un ViewModel per le istanze in cui ho le proprietà di cui ho bisogno per la vista ma che potrebbero non essere necessariamente necessarie nell'entità (database).

Http I metodi del controller Get sono immediati ActionResults con return View(repository.FetchNewViewModel()) per Create o repository.FetchModelById(id) per Modifica. In entrambi i casi, sto inizializzando le mie entità prima di passarle alla vista.

Create([Bind(Exclude = "Entity.EntityId")] ViewModel model) e Edit(ViewModel model) sono i metodi del controller Post Http di Creare e modificare. La mia vista Modifica ha un campo di input nascosto per EntityId per passarlo avanti e indietro.

Quando il metodo Http Post ha il viewmodel, perdo tutti i valori Entity.Relation e ViewModel. (Multi) SelectList. Devo ricostruire l'oggetto se voglio che la mia vista per visualizzare correttamente: `

try 
{ 
    var tags = model.TagIds; // List<string> or <int> depending on your Id type 
    if (model.TagsList == null) // It will be 
    { 
     model.TagsList = _repository.FetchSelectedTags(tags); // Build a new SelectList 
    } 

    if (!ModelState.IsValid) 
    { 
     return View(model); 
    } 

    _repository.Add(model.Article, tags); // or Save() for Edit 
} 
catch 
{ 
    return View(model); // Generally means something screwed in the repository class 
} 

return RedirectToAction("Index"); 

`

C'è forse il 30% della mia base dell'entità utilizzando un ViewModel quindi ho sicuramente uso solo se necessario. Se nella maggior parte dei casi sono presenti viste complesse e dati di modelli, è probabile che sia possibile suddividerli in visualizzazioni più piccole.

5
  1. Avere la tua mappa del modello di vista one-to-one con il tuo modello di dominio.

  2. Specificare Model come argomento per routeValues come di seguito. Ciò significa che il modello di visualizzazione verrà inizializzato con i valori del modello di dominio. Solo il sottoinsieme di campi nel modulo verrà sovrascritto nel risultato personViewData.

Aggiornamento Vista:

@model ViewModel.PersonView 

@using (Html.BeginForm("Update", "Profile", Model, FormMethod.Post)) 
{ 
    ...Put your sub set of the PersonView fields here 
} 

ProfileController:

public ActionResult Update(string userName) 
{ 
    Person person = _unitOfWork.Person.Get().Where(p => p.UserName == userName).FirstOrDefault(); 
    PersonView personView = new PersonView(); 
    Mapper.Map(person, personView); 

    return View(personView); 
} 

[HttpPost] 
public ActionResult Update(PersonView personViewData) 
{ 
    Person person = _unitOfWork.Person.Get().Where(p => p.UserName == personViewData.UserName).FirstOrDefault(); 
    Mapper.Map(personViewData, person); 
    _unitOfWork.Person.Update(person); 
    _unitOfWork.Save(); 

    return Json(new { saved = true, status = "" }); 
} 
Problemi correlati