4

J'ai un problème de liaison de modèle dans mon application ASP.NET MVC 2 RC qui utilise NHibernate pour l'accès aux données. Nous essayons de construire l'application de manière Ruby on Rails et avons une architecture très simple où les entités de domaine sont utilisées depuis la base de données jusqu'à la vue.Liaison de modèle ASP.NET MVC 2 RC avec NHibernate et listes déroulantes

L'application a deux entités de domaine qui peut être illustré par les deux catégories suivantes:

public class Product { 
    ... 

    public Category Category { get; set; }  
} 

public class Category { 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

Dans la vue qui rend le formulaire de modification a fait la déclaration suivante pour afficher une liste déroulante:

<%= Html.DropDownListFor(model => model.Category.Id, 
     new SelectList(ViewData["categories"] as IList<Category>, "Id", "Name"), 
     "-- Select Category --") %> 

Veuillez ne pas tenir compte de l'utilisation de données d'affichage «non typées» pour contenir la collection de catégories.

La méthode d'action qui reçoit le message de formulaire est similaire à la suivante. Notez que l'attribut TransactionFilter ajoute la gestion des transactions NHibernate et valide la transaction si aucune exception ne se produit et que la validation réussit.

[HttpPost] 
[TransactionFilter] 
public ActionResult Edit(int id, FormCollection collection) { 
    var product = _repository.Load(id); 

    // Update the product except the Id 
    UpdateModel(product, null, null, new[] {"Id"}, collection); 

    if (ModelState.IsValid) { 
     return RedirectToAction("Details", new {id}); 
    } 
    return View(product); 
} 

Mon problème est que le product.Category.Id est défini avec la valeur sélectionnée dans le formulaire, par ex. Category.Id = "2". En utilisant les résultats de liant modèle par défaut dans le type d'exception NHibernate suivante:

identifier of an instance of Name.Space.Entities.Category was altered from 4 to 2 

Cela fait beaucoup de sens puisque le produit a déjà une catégorie attribuée et que la clé primaire de cette catégorie existante est en cours de modification. Une autre instance de catégorie aurait dû être affectée à la place.

Je suppose qu'un ModelBinder personnalisé peut être créé pour gérer ce problème, mais existe-t-il un moyen plus simple de le faire fonctionner? Can (et devrait) les entités de domaine être modifiées pour gérer cela?

+0

Avez-vous trouvé une solution à ce problème? J'ai exactement le même problème. – nabeelfarid

+0

Non, je n'ai jamais trouvé une bonne solution (cependant, nous n'avons pas passé beaucoup de temps à la chercher). Nous avons traité le problème explicitement en dehors du classeur. – HakonB

+0

Pourriez-vous s'il vous plaît me donner un indice sur la façon dont vous manipulez cela en dehors du classeur? N'utilisez-vous pas TryUpdateModel/UpdateModel? – nabeelfarid

Répondre

0

La solution que nous avons choisi à l'époque était quelque chose de semblable à ceci:

TryUpdateModel(product, null, null, new[] {"Category"}, collection); 
int categoryId; 
if (int.TryParse(collection["Category.Id"], NumberStyles.Integer, CultureInfo.InvariantCulture, out categoryId) && categoryId > 0) { 
    product.Category = _categoryRepository.Load(categoryId); 
} 
else { 
    product.Category = null; 
} 

Nous disons simplement le modèle de liaison pour exclure la propriété d'association et poignée manuellement. Pas assez, mais a travaillé à l'époque ....

0

J'ai utilisé des techniques similaires avec les classes Linq to SQL auparavant sans problème. Je ne pense pas que vous auriez besoin d'un ModelBinder personnalisé pour cela. UpdateModel doit mettre à jour la classe de produit que vous lui transmettez, et non la sous-classe Catégorie qui lui est associée. Vérifiez le code HTML généré par l'assistant DropDownListFor. Quel est le nom de l'élément? Il doit s'agir du nom du champ de clé étrangère dans votre table Products (par exemple "CategoryID" ou "Product.CategoryID" et non "Category.Id"). Si c'est "Category.Id" - essayez de changer le premier paramètre de DropDownListFor en "model => model.Category" ou "model => model.CategoryID" (ou quel que soit le champ de la clé étrangère). Cela doit provoquer UpdateModel pour mettre à jour uniquement le champ de clé étrangère dans la classe Product et non l'ID de classe Category.

+1

La clé étrangère n'est pas disponible car la relation est entièrement gérée par NHibernate. J'ai fini par gérer manuellement le cas de catégorie et ne pas créer un classeur de modèle personnalisé – HakonB

2

Je résolu un problème similaire avec zones de liste déroulante ma page d'édition en changeant la ligne suivante

@Html.DropDownListFor(x => x.Employee.Id, new SelectList(ViewBag.Employees, "Id", "DisplayName")) 

par

@Html.DropDownListFor(x => x.Employee, new SelectList(ViewBag.Employees, "Id", "DisplayName")) 

J'enlevé le ". Id" comme Bryan suggéré.Avant le changement, le modèle ne contenait que l'identifiant de l'employé et après le changement, le classeur a également chargé tous les détails de l'employé dans le modèle.