代码之家  ›  专栏  ›  技术社区  ›  James Kolpack

具有自定义格式的ASP.NET MVC视图模型映射

  •  6
  • James Kolpack  · 技术社区  · 15 年前

    我正在处理的项目在域模型中有大量的货币属性,我需要将它们格式化为 $#,###.## 用于在视图中来回传输。我对可以使用的不同方法有自己的看法。一种方法是在视图中显式格式化值,如 "Pattern 1" from Steve Michelotti

    DRY principle 很快。

    ASP.NET MVC in Action 第4.4.1节和 "Pattern 3"

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

    然后将其映射为:

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

    ... 但是我觉得我不需要显式地定义它从哪个属性映射 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>()
    

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

    我还想了解一下在视图中实现自定义格式的其他策略。


    ASP.NETMVC正在运行 :

    一开始,我们可能想通过考试 视图,但日期时间?性质 [在模型中]将导致问题。 例如,我们需要选择一个 为它们设置格式,例如 视图将被强制执行null 无效的观点很难统一 测试,所以我们想让他们保持苗条 视图是传递给 响应流,仅限使用 是指当 ConferenceForm视图模型对象是一个 这方面的例子。上市公告 4.14所有属性都是字符串。我们会把日期安排妥当的 在此视图模型之前格式化 对象放置在视图数据中。这 方式、观点不必考虑 对象,它可以格式化 正确地提供信息。

    3 回复  |  直到 15 年前
        1
  •  2
  •   Jimmy Bogard    15 年前

    您需要的是自定义类型转换器:

    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
       }
    }
    
        2
  •  6
  •   tvanfosson    15 年前

    public static string ToMoney( this decimal source )
    {
        return string.Format( "{0:c}", source );
    }
    
    
    <%= Model.CurrencyProperty.ToMoney() %>
    

    因为这显然是一个与视图相关(与模型无关)的问题,所以如果可能的话,我会尽量将其保留在视图中。这基本上将它移动到decimal上的扩展方法,但用法在视图中。您还可以执行HtmlHelper扩展:

    public static string FormatMoney( this HtmlHelper helper, decimal amount )
    {
        return string.Format( "{0:c}", amount );
    }
    
    
    <%= Html.FormatMoney( Model.CurrencyProperty ) %>
    

        3
  •  3
  •   James Sullivan    13 年前

    您是否考虑过在ViewModel上添加DisplayFormat?这就是我所使用的,它快速而简单。

    ViewModel :
        [DisplayFormat(DataFormatString = "{0:c}", ApplyFormatInEditMode = true)]
        public decimal CurrencyProperty { get; set; }
    
    
    View :
        @Html.DisplayFor(m => m.CurrencyProperty)