Il progetto su cui sto lavorando ha un gran numero di proprietà di valuta nel modello di dominio e ho bisogno di formattarli come $#,###.##
per la trasmissione da e verso la vista. Ho avuto una visione di opinioni su diversi approcci che potrebbero essere utilizzati. Un approccio potrebbe essere quello di formattare i valori esplicitamente all'interno della vista, come in "Pattern 1" from Steve Michelotti:ASP.NET MVC ViewModel mapping con formattazione personalizzata
... ma questo inizia a violare DRY principle molto rapidamente.
L'approccio preferito sembra essere quello di eseguire la formattazione durante la mappatura tra DomainModel e un ViewModel (come da ASP.NET MVC in Action sezione 4.4.1 e "Pattern 3"). Utilizzando automapper, questo si tradurrà in un certo codice come il seguente:
[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);
}
}
Utilizzando IValueFormatter
questo modo funziona alla grande. Ora, come mapparlo da DomainModel a ViewModel? Ho provato con un costume 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);
}
}
E quindi mappati con:
// from vm to dm
Mapper.CreateMap<ViewModel, DomainModel>()
.ForMember(dm => dm.CurrencyProperty,
mc => mc
.ResolveUsing<CurrencyResolver>()
.FromMember(vm => vm.CurrencyProperty));
in grado di soddisfare questa prova:
///<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));
}
... Ma io sento che Non dovrei aver bisogno di definire esplicitamente la proprietà con cui viene mappato da FromMember
dopo aver fatto ResolveUsing
poiché le proprietà hanno lo stesso nome - c'è un migliore modo per definire questa mappatura? Come ho già detto, ci sono un buon numero di proprietà con valori di valuta che dovranno essere mappati in questo modo.
Detto questo: esiste un modo per risolvere automaticamente questi mapping definendo alcune regole a livello globale? Le proprietà ViewModel sono già decorate con DataAnnotation
attributi [DataType(DataType.Currency)]
per la convalida, quindi speravo che potrei definire una regola che fa:
if (destinationProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyFormatter>()
if (sourceProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyResolver>()
... in modo che possa ridurre al minimo la quantità di configurazione boilerplate per ciascuna delle tipi di oggetti.
Sono anche interessato a conoscere eventuali strategie alternative per eseguire la formattazione personalizzata da-e-dalla vista.
In un primo momento ci potrebbe essere tentati di passare questo semplice oggetto dritto alla vista , ma il DateTime? le proprietà [nel Modello] causeranno problemi. Per esempio, dobbiamo scegliere una formattazione per loro come ToShortDateString() o ToString(). La vista sarebbe forzata a fare il controllo nullo per mantenere lo schermo da saltando su quando le proprietà sono null. Le viste sono difficili da testare con l'unità , quindi vogliamo tenerle sottili come possibile.Poiché l'output di una vista è una stringa passata al flusso di risposta , utilizzeremo solo gli oggetti che sono compatibili con le stringhe; che è, oggetti che non falliranno mai quando ToString() viene richiamato su di essi. L'oggetto del modello di visualizzazione ConferenceForm è un esempio di . Si noti nella lista 4.14 che tutte le proprietà sono stringhe. Avremo le date correttamente formattate prima che l'oggetto modello vista venga inserito nei dati di visualizzazione. Questo modo , la vista non deve prendere in considerazione l'oggetto e può formattare correttamente le informazioni .
<% = string.Format ("{0: c}", Model.CurrencyProperty)%> sembra piuttosto a me. Forse mi ci sono abituato ... –