加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc – 使用自定义格式的ASP.NET MVC ViewModel映射

发布时间:2020-12-15 20:29:59 所属栏目:asp.Net 来源:网络整理
导读:我正在处理的项目在域模型中有大量的货币属性,我需要将它们格式化为$#,###.##,用于传输到视图和从视图传输.我已经对可以使用的不同方法有了一些看法.一种方法可以是在视图中显式格式化值,如 “Pattern 1” from Steve Michelotti中所示: ……但这很快就违反
我正在处理的项目在域模型中有大量的货币属性,我需要将它们格式化为$#,###.##,用于传输到视图和从视图传输.我已经对可以使用的不同方法有了一些看法.一种方法可以是在视图中显式格式化值,如 “Pattern 1” from Steve Michelotti中所示:

……但这很快就违反了DRY principle.

首选方法似乎是在DomainModel和ViewModel之间的映射期间进行格式化(根据ASP.NET MVC in 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);
 }
}

使用IValueFormatter这种方式效果很好.现在,如何将它从DomainModel映射回ViewModel?我尝试过使用自定义类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);
 }
}

然后将其映射为:

// from vm to dm
  Mapper.CreateMap<ViewModel,DomainModel>()
   .ForMember(dm => dm.CurrencyProperty,mc => mc
     .ResolveUsing<CurrencyResolver>()
     .FromMember(vm => vm.CurrencyProperty));

哪个会满足这个测试:

///<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));
 }

…但我觉得在执行ResolveUsing之后我不需要显式定义它与FromMember映射的属性,因为属性具有相同的名称 – 是否有更好的方法来定义此映射?正如我所提到的,有许多具有货币值的属性需要以这种方式映射.

话虽如此 – 有没有办法通过全局定义一些规则来自动解决这些映射? 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>()

…这样我就可以最小化每种对象类型的样板设置量.

我也有兴趣听到任何可以在View中完成自定义格式化的替代策略.

从ASP.NET MVC in Action:

At first we might be tempted to pass
this simple object straight to the
view,but the DateTime? properties
[in the Model] will cause problems.
For instance,we need to choose a
formatting for them such as
ToShortDateString() or ToString(). The
view would be forced to do null
checking to keep the screen from
blowing up when the properties are
null. Views are difficult to unit
test,so we want to keep them as thin
as possible. Because the output of a
view is a string passed to the
response stream,we’ll only use
objects that are stringfriendly; that
is,objects that will never fail when
ToString() is called on them. The
ConferenceForm view model object is an
example of this. Notice in listing
4.14 that all of the properties are strings. We’ll have the dates properly
formatted before this view model
object is placed in view data. This
way,the view need not consider the
object,and it can format the
information properly.

解决方法

您正在寻找自定义TypeConverter:
Mapper.CreateMap<string,decimal>().ConvertUsing<MoneyToDecimalConverter>();

然后创建转换器:

public class MoneyToDecimalConverter : TypeConverter<string,decimal>
{
   protected override decimal ConvertCore(string source)
   {
      // magic here to convert from string to decimal
   }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读