web-dev-qa-db-fra.com

Créer une liste déroulante pour MVC3 à l'aide d'Entity Framework (modèle .edmx), de vues de rasoir et de insérer un enregistrement de base de données dans plusieurs tables

Après avoir lu des centaines d'articles sur la création d'une liste déroulante dans MVC 3 avec Razor Views, je n'ai pas pu en trouver un qui convienne à mon cas.

Situation: J'essaye finalement de créer une vue pour ajouter un employé à une base de données.

Voici une image du modèle .EDMX que j'utilise (les tables qui seront utilisées par create ().):

enter image description here

Objectifs:

  1. Créer un employé (le fichier Create.cshtml (fortement typé) est créé avec une vue partielle pour les cases à cocher StaffNotify) {J'utilise un @model distinct dans la vue partielle Notify de la vue Créer et je ne suis pas sûr que ce soit sûr ??? @model ShadowVenue.Models.Employee & @model ShadowVenue.Models.StaffNotify)

  2. Créez une liste déroulante pour StaffTypeId (qui insérera la valeur [StaffTypeId] de la table "StaffType" (qui a une relation de 1 à plusieurs), mais affichera la valeur de chaîne [Type] dans la liste déroulante)

  3. Créez une liste déroulante pour GenderId (qui insérera la valeur [GenderId] de la table "Genres" (qui a une relation de 1 à plusieurs), mais affichera la valeur de chaîne [Gender] dans la liste déroulante)

  4. Insérer l'enregistrement dans la base de données (j'ai les notifications relatives au personnel dans un tableau séparé avec une relation 1 pour 1 sur la clé primaire StaffId)

Je semble avoir des problèmes avec le code du contrôleur pour cela.

Je ne sais pas si je devrais créer une procédure stockée dans le modèle EDMX, ou proposer une syntaxe de requête ou de méthode, sans savoir quelle est la meilleure méthode.

C'est ma première grande application MVC3 utilisant le modèle Entity Framework.

(Si vous avez besoin de connaître l'un des noms de propriété de navigation afin de vous aider avec la solution, faites le moi savoir, je vous les fournirai)

49
Timothy Green

Ne transmettez pas les modèles de base de données directement à vos vues. Vous avez la chance d'utiliser MVC, encapsulez-vous donc à l'aide de modèles de vue.

Créez une classe de modèle de vue comme ceci:

public class EmployeeAddViewModel
{
    public Employee employee { get; set; }
    public Dictionary<int, string> staffTypes { get; set; }
    // really? a 1-to-many for genders
    public Dictionary<int, string> genderTypes { get; set; }

    public EmployeeAddViewModel() { }
    public EmployeeAddViewModel(int id)
    {
        employee = someEntityContext.Employees
            .Where(e => e.ID == id).SingleOrDefault();

        // instantiate your dictionaries

        foreach(var staffType in someEntityContext.StaffTypes)
        {
            staffTypes.Add(staffType.ID, staffType.Type);
        }

        // repeat similar loop for gender types
    }
}

Manette:

[HttpGet]
public ActionResult Add()
{
    return View(new EmployeeAddViewModel());
}

[HttpPost]
public ActionResult Add(EmployeeAddViewModel vm)
{
    if(ModelState.IsValid)
    {
        Employee.Add(vm.Employee);
        return View("Index"); // or wherever you go after successful add
    }

    return View(vm);
}

Enfin, enfin, dans votre vue (que vous pouvez d’abord utiliser Visual Studio pour l’échafauder), remplacez le type hérité par ShadowVenue.Models.EmployeeAddViewModel. De plus, là où vont les listes déroulantes, utilisez:

@Html.DropDownListFor(model => model.employee.staffTypeID,
    new SelectList(model.staffTypes, "ID", "Type"))

et de même pour la liste déroulante des sexes

@Html.DropDownListFor(model => model.employee.genderID,
    new SelectList(model.genderTypes, "ID", "Gender"))

Mise à jour par commentaires

Pour le genre, vous pouvez également le faire si vous pouvez vous passer de genderTypes dans le modèle de vue suggéré ci-dessus (bien que, à la réflexion, je générerais peut-être ce côté serveur dans le modèle de vue sous la forme IEnumerable). Donc, à la place de new SelectList... ci-dessous, vous utiliseriez votre IEnumerable.

@Html.DropDownListFor(model => model.employee.genderID,
    new SelectList(new SelectList()
    {
        new { ID = 1, Gender = "Male" },
        new { ID = 2, Gender = "Female" }
    }, "ID", "Gender"))

Enfin, une autre option est une table de consultation. Fondamentalement, vous conservez les paires clé-valeur associées à un type de recherche. Un exemple de type peut être le genre, tandis qu'un autre peut être État, etc. J'aime structurer le mien comme suit:

ID | LookupType | LookupKey | LookupValue | LookupDescription | Active
1  | Gender     | 1         | Male        | male gender       | 1
2  | State      | 50        | Hawaii      | 50th state        | 1
3  | Gender     | 2         | Female      | female gender     | 1
4  | State      | 49        | Alaska      | 49th state        | 1
5  | OrderType  | 1         | Web         | online order      | 1

J'aime utiliser ces tables lorsqu'un ensemble de données ne change pas très souvent, mais doit encore être énuméré de temps en temps.

J'espère que cela t'aides!

75
David Fox

En fait, je dois dire que David a raison avec sa solution, mais certains sujets me dérangent:

  1. Vous ne devriez jamais envoyer votre modèle à la vue => Ceci est correct
  2. Si vous créez un ViewModel et incluez le modèle en tant que membre dans le ViewModel, alors vous avez effectivement envoyé votre modèle à la vue => c'est MAUVAIS
  3. Utilisation de dictionnaires pour envoyer les options à la vue => ce style n'est pas bon

Alors, comment pouvez-vous créer un meilleur couplage?

J'utiliserais un outil comme AutoMapper ou ValueInjecter pour mapper entre ViewModel et Model. AutoMapper semble avoir la syntaxe et la convivialité meilleures, mais la version actuelle ne contient pas de sujet très grave: il n’est pas possible d’effectuer le mappage de ViewModel à Model (dans certaines circonstances, comme aplatissement, etc., mais ceci est hors sujet) Donc pour le moment je préfère utiliser ValueInjecter.

Vous créez donc un ViewModel avec les champs dont vous avez besoin dans la vue. Vous ajoutez les éléments SelectList dont vous avez besoin en tant que recherches. Et vous les ajoutez déjà en tant que SelectLists. Vous pouvez donc interroger une source compatible LINQ, sélectionner l'ID et le champ de texte et les stocker en tant que liste de sélection: vous gagnez le besoin de créer un nouveau type (dictionnaire) en tant que recherche et vous ne faites que déplacer le new SelectList de la vue au contrôleur.

  // StaffTypes is an IEnumerable<StaffType> from dbContext
  // viewModel is the viewModel initialized to copy content of Model Employee  
  // viewModel.StaffTypes is of type SelectList

  viewModel.StaffTypes =
    new SelectList(
        StaffTypes.OrderBy( item => item.Name )
        "StaffTypeID",
        "Type",
        viewModel.StaffTypeID
    );

Dans la vue, il suffit d'appeler

@Html.DropDownListFor( model => mode.StaffTypeID, model.StaffTypes )

De retour dans l'élément post de votre méthode dans le contrôleur, vous devez prendre un paramètre du type de votre ViewModel. Vous vérifiez ensuite pour la validation. Si la validation échoue, vous devez vous rappeler de renseigner de nouveau la liste de sélection viewModel.StaffTypes, Car cet élément sera null lors de l'entrée dans la fonction post. J'ai donc tendance à séparer ces éléments de population en une fonction. Vous venez de rappeler return new View(viewModel) si quelque chose ne va pas. Les erreurs de validation détectées par MVC3 seront automatiquement affichées dans la vue.

Si vous avez votre propre code de validation, vous pouvez ajouter des erreurs de validation en spécifiant le champ auquel ils appartiennent. Consultez la documentation sur ModelState pour obtenir des informations à ce sujet.

Si le viewModel est valide, vous devez effectuer l'étape suivante:

S'il s'agit d'une création d'un nouvel élément, vous devez renseigner un modèle à partir de viewModel (le mieux adapté est ValueInjecter). Vous pouvez ensuite l'ajouter à la collection EF de ce type et valider les modifications.

Si vous avez une mise à jour, vous obtenez d'abord l'élément de base de données actuel dans un modèle. Ensuite, vous pouvez copier les valeurs de viewModel dans le modèle (utilisez à nouveau ValueInjecter pour le faire très rapidement). Après cela, vous pouvez SaveChanges et vous avez terminé.

N'hésitez pas à demander si quelque chose n'est pas clair.

23
nttakr