Le projet sur lequel je travaille comporte un grand nombre de propriétés de devise dans le modèle de domaine et je dois les formater en tant que $#,###.##
pour la transmission vers et depuis la vue. J'ai eu un avis sur les différentes approches qui pourraient être utilisées. Une approche pourrait être de formater les valeurs explicitement dans la vue, comme dans "Pattern 1" from Steve Michelotti:Mappage ASP.NET MVC ViewModel avec mise en forme personnalisée
... mais cela commence DRY principle violer très rapidement.
L'approche préférée semble être de faire la mise en forme pendant le mappage entre DomainModel et un ViewModel (selon ASP.NET MVC in Action section 4.4.1 et "Pattern 3"). En utilisant AutoMapper, cela se traduira par un code comme suit:
[TestFixture]
public class ViewModelTests
{
[Test]
public void DomainModelMapsToViewModel()
{
var domainModel = new DomainModel {CurrencyProperty = 19.95m};
var viewModel = new ViewModel(domainModel);
Assert.That(viewModel.CurrencyProperty, Is.EqualTo("$19.95"));
}
}
public class DomainModel
{
public decimal CurrencyProperty { get; set; }
}
public class ViewModel
{
///<summary>Currency Property - formatted as $#,###.##</summary>
public string CurrencyProperty { get; set; }
///<summary>Setup mapping between domain and view model</summary>
static ViewModel()
{
// map dm to vm
Mapper.CreateMap<DomainModel, ViewModel>()
.ForMember(vm => vm.CurrencyProperty, mc => mc.AddFormatter<CurrencyFormatter>());
}
/// <summary> Creates the view model from the domain model.</summary>
public ViewModel(DomainModel domainModel)
{
Mapper.Map(domainModel, this);
}
public ViewModel() { }
}
public class CurrencyFormatter : IValueFormatter
{
///<summary>Formats source value as currency</summary>
public string FormatValue(ResolutionContext context)
{
return string.Format(CultureInfo.CurrentCulture, "{0:c}", context.SourceValue);
}
}
utilisant IValueFormatter
cette façon fonctionne très bien. Maintenant, comment le mapper du DomainModel à ViewModel? Je l'ai essayé d'utiliser une coutume class CurrencyResolver : ValueResolver<string,decimal>
public class CurrencyResolver : ValueResolver<string, decimal>
{
///<summary>Parses source value as currency</summary>
protected override decimal ResolveCore(string source)
{
return decimal.Parse(source, NumberStyles.Currency, CultureInfo.CurrentCulture);
}
}
Et puis cartographié avec:
// from vm to dm
Mapper.CreateMap<ViewModel, DomainModel>()
.ForMember(dm => dm.CurrencyProperty,
mc => mc
.ResolveUsing<CurrencyResolver>()
.FromMember(vm => vm.CurrencyProperty));
qui satisfera ce test:
///<summary>DomainModel maps to ViewModel</summary>
[Test]
public void ViewModelMapsToDomainModel()
{
var viewModel = new ViewModel {CurrencyProperty = "$19.95"};
var domainModel = new DomainModel();
Mapper.Map(viewModel, domainModel);
Assert.That(domainModel.CurrencyProperty, Is.EqualTo(19.95m));
}
... Mais je me sens que Je ne devrais pas besoin de définir explicitement la propriété à partir de laquelle FromMember
est mappé après avoir effectué ResolveUsing
car les propriétés ont le même nom - y a-t-il un meilleur façon de définir cette cartographie? Comme je l'ai mentionné, il y a un bon nombre de propriétés avec des valeurs monétaires qui devront être cartographiées de cette façon. Ceci étant dit, y a-t-il un moyen de résoudre automatiquement ces mappages en définissant une règle globalement? Les propriétés ViewModel sont déjà décorées avec DataAnnotation
attributs [DataType(DataType.Currency)]
pour la validation, donc j'espérais que je pourrais définir une règle qui fait:
if (destinationProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyFormatter>()
if (sourceProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyResolver>()
... pour que je puisse minimiser la quantité de configuration boilerplate pour chacun des types d'objets.
Je suis également intéressé par toute autre stratégie pour réaliser un formatage personnalisé à partir de la vue.
Au début, on pourrait être tenté de passer ce simple objet droit à la vue , mais le DateTime? propriétés [dans le modèle] entraînera des problèmes. Par exemple, nous devons choisir une mise en forme pour eux comme ToShortDateString() ou ToString(). La vue serait obligée de faire la null vérification pour garder l'écran de explose lorsque les propriétés sont null. Les vues sont difficiles à l'unité test , donc nous voulons les garder aussi minces que possible.Étant donné que la sortie d'une vue est une chaîne transmise au flux de réponse , nous utiliserons uniquement des objets stringfriendly; est, objets qui n'échoueront jamais lorsque ToString() leur est appelée. L'objet modèle de vue ConferenceForm est un exemple de . Notez dans la liste 4.14 que toutes les propriétés sont des chaînes. Nous aurons les dates correctement formaté avant que ce modèle de vue objet est placé dans les données de vue. Cette façon , la vue ne doit pas prendre en compte l'objet , et il peut formater les informations correctement.
<% = chaîne.Format ("{0: c}", Model.CurrencyProperty)%> me semble bien. Peut-être que je suis juste habitué ... –