Jam*_*ack 6 asp.net-mvc formatting automapper
我正在处理的项目在域模型中有大量的货币属性,我需要格式化这些属性$#,###.##以便传输到视图和从视图传输.我已经对可以使用的不同方法有了一些看法.一种方法可能是在视图中显式格式化值,如Steve Michelotti的"Pattern 1":
......但这很快就开始违反DRY原则.
首选方法似乎是在DomainModel和ViewModel之间的映射期间进行格式化(根据ASP.NET MVC的Action第4.4.1节和"Pattern 3").使用AutoMapper,这将导致一些代码如下所示:
[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);
}
}
Run Code Online (Sandbox Code Playgroud)
使用IValueFormatter这种方式很有效.现在,如何将它从DomainModel映射回ViewModel?我尝试过使用自定义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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后将其映射为:
// from vm to dm
Mapper.CreateMap<ViewModel, DomainModel>()
.ForMember(dm => dm.CurrencyProperty,
mc => mc
.ResolveUsing<CurrencyResolver>()
.FromMember(vm => vm.CurrencyProperty));
Run Code Online (Sandbox Code Playgroud)
哪个会满足这个测试:
///<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));
}
Run Code Online (Sandbox Code Playgroud)
...但我感觉,我不应该需要明确界定其性质是否正在从与映射FromMember后做事ResolveUsing,因为属性具有相同的名称-有没有更好的方法来定义这种映射?正如我所提到的,有许多具有货币值的属性需要以这种方式映射.
话虽如此 - 有没有办法通过全局定义一些规则来自动解决这些映射?ViewModel属性已经用验证DataAnnotation属性修饰[DataType(DataType.Currency)],所以我希望我可以定义一些规则:
if (destinationProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyFormatter>()
if (sourceProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyResolver>()
Run Code Online (Sandbox Code Playgroud)
...这样我就可以最小化每种对象类型的样板设置量.
我也有兴趣听到任何可以在View中完成自定义格式化的替代策略.
起初我们可能想把这个简单的对象直接传递给视图,但是DateTime?属性[在模型中]将导致问题.例如,我们需要为它们选择格式,例如ToShortDateString()或ToString().当属性为null时,视图将被强制执行空检查以防止屏幕爆炸.视图很难进行单元测试,因此我们希望尽可能保持它们的薄.因为视图的输出是传递给响应流的字符串,所以我们只使用对字符串友好的对象; 也就是说,在对它们调用ToString()时永远不会失败的对象.ConferenceForm视图模型对象就是一个例子.请注意清单4.14中所有属性都是字符串.在将视图模型对象放置在视图数据中之前,我们将正确格式化日期.这样,视图不需要考虑对象,它可以正确地格式化信息.
您是否考虑过使用扩展方法来格式化资金?
public static string ToMoney( this decimal source )
{
return string.Format( "{0:c}", source );
}
<%= Model.CurrencyProperty.ToMoney() %>
Run Code Online (Sandbox Code Playgroud)
由于这显然是与视图相关的(与模型无关的)问题,因此如果可能的话,我会尝试将其保留在视图中.这基本上将它移动到十进制的扩展方法,但用法在视图中.您还可以执行HtmlHelper扩展:
public static string FormatMoney( this HtmlHelper helper, decimal amount )
{
return string.Format( "{0:c}", amount );
}
<%= Html.FormatMoney( Model.CurrencyProperty ) %>
Run Code Online (Sandbox Code Playgroud)
如果你更喜欢那种风格.它更像View,因为它是一个HtmlHelper扩展.
您正在寻找自定义 TypeConverter:
Mapper.CreateMap<string, decimal>().ConvertUsing<MoneyToDecimalConverter>();
Run Code Online (Sandbox Code Playgroud)
然后创建转换器:
public class MoneyToDecimalConverter : TypeConverter<string, decimal>
{
protected override decimal ConvertCore(string source)
{
// magic here to convert from string to decimal
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5425 次 |
| 最近记录: |