2009-05-21 17 views
12

Sto utilizzando ASP.NET MVC e ADO.NET Entity Framework insieme.Entity Framework con MVC fortemente tipizzato

Desidero fortemente che le mie viste e i controller siano stati digitati in modo corretto.

Ma come faccio a gestire le associazioni di entità?

Ecco un semplice esempio:

una persona ha un reparto. Un dipartimento ha zero o più persone.

Entity Data Model of Person and Department entities

mio controllore passa un'istanza di un oggetto Person e una collezione di tutto il Dipartimento di oggetti alla vista.

public class PersonController : Controller 
{ 
    ... 

    // 
    // GET: /Person/Create 

    public ActionResult Create() 
    { 
     Person Model = new Person(); 
     Model.Id = Guid.NewGuid(); 
     ViewData["Departments"] = db.Department; 
     return View(Model); 
    } 
    ... 
} 

La mia vista ha un DropDownList "Dipartimento" con tutti i reparti come opzioni.

<% using (Html.BeginForm()) {%> 

    <fieldset> 
     <legend>Fields</legend> 
     <p> 
      <label for="Id">Id:</label> 
      <%= Html.TextBox("Id") %> 
      <%= Html.ValidationMessage("Id", "*") %> 
     </p> 
     <p> 
      <label for="Name">Name:</label> 
      <%= Html.TextBox("Name") %> 
      <%= Html.ValidationMessage("Name", "*") %> 
     </p> 
     <p> 
      <label for="Department">Family:</label> 
      <%= Html.DropDownList("Department", new SelectList((IEnumerable)ViewData["Departments"], "Id", "Name"))%> 
      <%= Html.ValidationMessage("Department", "*")%> 
     </p> 
     <p> 
      <input type="submit" value="Create" /> 
     </p> 
    </fieldset> 

<% } %> 

Ma, l'oggetto Person inviato al controller ha non Dipartimento e fallisce!

public class PersonController : Controller 
{ 
    ... 

    // 
    // POST: /Person/Create 

    [AcceptVerbs(HttpVerbs.Post)] 
    public ActionResult Create(Person Model) 
    { 
     try 
     { 
      db.AddToPerson(Model); 
      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 
     catch 
     { 
      return View(); 
     } 
    } 
    ... 
} 

Perché non è il Dipartimento selezionato da DropDownList "Dipartimento", ha aggiunto automaticamente alla Persona modello?

Come utilizzare ADO.NET Entity Framework e ASP.NET MVC con viste e controller fortemente tipizzati?

+0

http://stackoverflow.com/questions/922402/strongly-typed-asp-net-mvc-with -ado-net-entity-framework –

risposta

2

Non ho trovato che l'helper DropDownList out-of-the-box funzioni molto bene con il binding del modello. Devo sempre prendere il valore di DropDownList da ViewData e mappare il valore manualmente nel mio controller prima di commettere le modifiche al repository o al db.

La maggior parte delle volte sto utilizzando una DropDownList per mostrare le opzioni per una relazione di tipo personalizzato (ad esempio i reparti di una persona). MVC non può sapere come mappare un oggetto basandosi semplicemente sul valore di un elemento selezionato in un elenco. Piuttosto, devi andare a prendere quell'entità usando il valore selezionato e mapparlo da solo. Non so se questo è il problema qui, e non ho provato il modello legando un elenco a un modello con tipi semplici come opzioni (come la quantità di prodotti da acquistare o qualcosa del genere), ma sarei curioso per saperne di più su questo particolare problema e su come gli altri stanno gestendo l'associazione dei modelli con elenchi a discesa.

+0

Non penso che il tuo problema sia isolato nell'utilizzo di EF con MVC. Ho lo stesso problema con POCOs. – nkirkes

5

cercherò di guidarvi dal lato MVC, come questo è dove il problema è, credo

Nel metodo Create() si crea l'oggetto Person e l'elenco dei servizi dal database, quindi entrambi gli oggetti vengono passati alla vista. La vista prende i dati dall'elenco Reparto e li utilizza per eseguire il rendering di un modulo HTML, utilizzando solo ID e Nome.

Nella fase successiva, il modulo viene inviato al server come una raccolta di coppie chiave-valore (POST standard). Il motore di routing preleva l'URL richiesto dall'attributo azione e lo risolve in azione PersonController.Create (Modello persona). L'argomento di questo metodo è Persona, quindi il raccoglitore dati viene attivato, crea la nuova istanza della classe Person e tenta di far corrispondere i dati in ingresso con le proprietà della Persona. Nel caso del dipartimento, il valore in entrata è l'ID del dipartimento (perché questo è ciò che si imposta come membro del valore per DropDownList), mentre il dipartimento delle proprietà della classe Person è probabilmente di tipo Dipartimento. Questa è una mancata corrispondenza, quindi non può riempire la proprietà e rimane vuota.

Come si può vedere, questa non è la limitazione di DropDownList, il problema è che non è possibile passare tutti i dati del Dipartimento a DropDownList e farlo ricreare durante il salvataggio (come con la Persona), a causa di una natura del POST richiesta, ed è per questo che DropDownList prende solo due valori da ogni dipartimento (valore e nome).

La mia solita soluzione: come normalmente i miei modelli non sono le stesse classi come i miei oggetti di business, lo faccio da avere due proprietà sul modello: ottenere unica proprietà IEnumerable, e un'altra proprietà IDdipartimento (get/set). Poi lo uso così:

<%= Html.DropDownList("DepartmentId", Model.Departments) %> 

Poi nell'azione salvare, mi afferra il Dipartimento dal db utilizzando DepartmentID, assegnarlo a persona e risparmiare.

Nel tuo caso (i modelli sono oggetti aziendali), probabilmente non proverei a collegare automaticamente il Reparto al modello Persona, ma semplicemente prendo l'ID e lo faccio da solo.

Questa è solo una supposizione (non sono uno specialista EF), ma penso che potresti avere un altro problema qui: se il db è un campo su Controller, ed è ricreato ad ogni richiesta, questo potrebbe essere un po ' sovraccarico inutile. Spero che non si apra una connessione db ogni volta, si prega di controllare.

Speranza che aiuta

3

Io uso un ViewModel (check out il tutorial NerdDinner, i riferimenti in fondo).

In primo luogo, è necessario fingere un vincolo di chiave esterna, estendendo il modello in un parziale di:

public partial class Person 
{ 
    public int DepartmentId 
    { 
    get 
    { 
     if(this.DepartmentsReference.EntityKey == null) return 0; 
     else 
     return (int)this.DepartmentsReference.EntityKey.EntityKeyValues[0].Value; 
    } 
    set 
    { 
     this.DepartmentsReference.EntityKey = 
      new EntityKey("YourEntities.DepartmentSet", "Id", value); 
    } 
    } 
} 

In secondo luogo, creare il ViewModel:

public class PersonFormViewModel 
{ 
    public SelectList Departments { get; set: } 
    public Person Pers { get; set; } 

    public PersonFormViewModel(Person p, List<Department> departments) 
    { 
    this.Pers = p; 
    this.Departments = new SelectList(departments, "Id", "Name", p.DepartmentId); 
    } 
} 

In terzo luogo, l'azione di controllo (un abbreviato creare esempio):

public ActionResult Create() 
{ 
    YourEntities entities = new YourEntities(); 
    List<Department> departments = entities.DepartmentSet.ToList(); 
    PersonFormViewModel viewModel = 
    new PersonFormViewModel(new Person(), departments); 
    return View(modelView); 
}  

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Create([Bind(Exclude="Id")]Person personToCreate 
{ 
    YourEntities entities = new YourEntities(); // Probably not instantiated here 
    entities.AddToPersonSet(personToCreate); 
    entities.SaveChanges(); 
    redirectToAction("Index"); 
} 

In quarto luogo, il frammento di vista:

<p> 
    <label for="Name">Name:</label> 
    <%= Html.TextBox("Name", Model.Pers.Name) %> 
    <%= Html.ValidationMessage("Name", "*") %> 
</p> 
<p> 
    <label for="DepartmentId">Family:</label> 
    <%= Html.DropDownList("DepartmentId", Model.Departments)%> 
    <%= Html.ValidationMessage("DepartmentId", "*")%> 
</p> 

Riferimenti:

+0

Ci dovrebbe essere un modo più semplice. MVC 2 ha una soluzione per questo? Immagina di creare una falsa chiave esterna per ogni ID esterno come DeparmentId. Il nostro codice diventa un po 'confuso. :-( –

Problemi correlati