2009-12-29 6 views
1

Dire que j'ai une classe, par exemple:associations ASP.NET MVC et NHibernate

public class Person 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual int SpouseId { get; set; } 
    public virtual Person Spouse { get; set; } 
} 

J'ai inclus le SpouseId parce que c'est la sortie-de-the-box pour modéliser liaison à l'aide (Try) UpdateModel (basé sur ScottGu's sample MVC 2.0 code here). Dans mon mappage NHibernate, j'ai défini SpouseId sur insert = false et update = false, et mon mapping marche très bien.

j'avoir un « créer personne » forme ASP.NET MVC (2), qui utilise ensuite SpouseId pour sélectionner le conjoint:

<% 
using (Html.BeginForm()) 
{ 
%> 
    <%= Html.LabelFor(m => m.Name) %> 
    <%= Html.TextBoxFor(m => m.Name) %> 
    <%= Html.ValidationMessageFor(m => m.Name) %> 

    <%= Html.LabelFor(m => m.SpouseId) %> 
    <%= Html.EditorFor(m => m.SpouseId, "PersonPicker") %> 
    <%= Html.ValidationMessageFor(m => m.SpouseId) %> 
<% 
} 
%> 

Là où j'ai un EditorTemplate pour « PersonPicker » que seulement affiche une liste déroulante basée sur une liste de personnes dans ViewData.

Lorsque je fais ensuite TryUpdateModel() cela fonctionne très bien, je reçois mes valeurs peuplées comme attendu, c'est-à-dire que SpouseId est réglé mais pas Spouse. et le problème avec ceci est bien sûr que NHibernate utilise Person.Spouse.

Je préférerais ignorer complètement le SpouseId puisqu'il encombre mon modèle et il est seulement là à cause de ce problème.

Je pourrais bien sûr faire person.Spouse = myService.Find<Person>(person.SpouseId); en code mais ça a une très, très mauvaise odeur.

Quelle est la meilleure façon de faire cela? De préférence, abandonner la propriété SpouseId entièrement.

(je pourrais ajouter que je suis en utilisant VS2008 et .NET 3.5)

Répondre

0

Ok ce que je vraiment, vraiment fini par faire était d'utiliser AutoMapper et ViewModel:

public class PersonViewModel 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public int SpouseId { get; set; }  
} 

Et peupler modèle de vue:

// set once for app Mapper.CreateMap<Person, PersonViewModel>(); 
var personViewModel = Mapper.Map<Person, PersonViewModel>(person); 

au domaine repeupler:

// set once for app 
// Mapper.CreateMap<PersonViewModel, Person>() 
// .ForMember(x => x.Id, o => o.Ignore()) 
// .ForMember(x => x.Spouse, o => o.MapFrom(x => PersonService.Find(x.SpouseId))); 
var viewModel = new PersonViewModel(); 
if (TryUpdateModel(viewModel)) 
{ 
    var person = PersonService.Find(viewModel.Id); 
    Mapper.Map(personViewModel, person); 
} 

La seule chose que je n'aime pas, c'est la "back-mapping" de Spouse, puisqu'elle nécessite un PersonService (par exemple. via DI/IoC) - mais en dehors de cela cela fonctionne.Pour cet exemple simple, AutoMapper est probablement trop lourd, et si nous n'utilisons pas AutoMapper pour le "sauvegarder dans le modèle de domaine", nous n'aurons pas la dépendance PersonService dans la configuration de mappage (cependant dans la méthode Action et cela pourrait "violer" DRY).

0

Voici ce que je l'ai fait jusqu'à présent cela fonctionne, mais je ne pense pas qu'il est parfait et pour cette raison je mes doutes quant à savoir si c'est la meilleure/bonne façon de le faire.

Je wen't la voie des ModelBinder et a créé un ModelBinder comme ce qui suit:

public class MyModelBinder : DefaultModelBinder 
{ 
    public MyModelBinder(IService service) 
    { 
     this.Service = service; 
    } 

    private IService Service 
    { 
     get; 
     set; 
    } 

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder) 
    { 
     var modelType = bindingContext.ModelType; 

     if (/* determine if modelType is a type for association */) 
     { 
      var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
      var id = (int?)value.ConvertTo(typeof(int)); 
      var result = id.HasValue ? this.Service.Find(modelType, id.Value) : null; 

      if (result != null) 
      { 
       return result; 
      } 
     } 

     return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder); 
    } 
} 

En dehors de la logique pour déterminer si le type est « inclus » il semble très bien avec moi.

La partie fastidieuse vient d'enregistrer les classeurs dans Global.asax (en utilisant StructureMap pour IoC):

ModelBinders.Binders[typeof(Person)] = ObjectFactory.GetInstance<MyModelBinder>(); 

Ce que je dois faire pour tous les types. Cela fonctionne et c'est mieux que ma solution précédente, mais pourrait-il être meilleur?