6

私が取り組んでいるプロジェクトには、ドメインモデルに多数の通貨プロパティがあり$#,###.##、ビューとの間で送受信するためにこれらをフォーマットする必要があります。私は、使用できるさまざまなアプローチについて考えてきました。1つのアプローチは、Steve Michelottiの「Pattern1」のように、ビュー内で値を明示的にフォーマットすることです。

...しかし、これはDRYの原則にすぐに違反し始めます。

推奨されるアプローチは、DomainModelとViewModelの間のマッピング中にフォーマットを実行することであるように見えます(アクションセクション4.4.1および「パターン3」のASP.NET MVCによる)。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にマップする方法を教えてください。カスタムを使ってみました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から:

最初は、この単純なオブジェクトをビューに直接渡したいと思うかもしれませんが、DateTime?[モデル内の]プロパティは問題を引き起こします。たとえば、ToShortDateString()やToString()などのフォーマットを選択する必要があります。プロパティがnullの場合に画面が拡大しないように、ビューはnullチェックを強制されます。ビューは単体テストが難しいため、できるだけ薄くしていきたいと考えています。ビューの出力は応答ストリームに渡される文字列であるため、文字列に適したオブジェクトのみを使用します。つまり、ToString()が呼び出されたときに失敗することのないオブジェクトです。ConferenceFormビューモデルオブジェクトはその一例です。リスト4.14で、すべてのプロパティが文字列であることに注意してください。このビューモデルオブジェクトがビューデータに配置される前に、日付が適切にフォーマットされます。このように、ビューはオブジェクトを考慮する必要がなく、情報を適切にフォーマットできます。

4

3 に答える 3

6

お金をフォーマットするために拡張方法を使用することを検討しましたか?

public static string ToMoney( this decimal source )
{
    return string.Format( "{0:c}", source );
}


<%= Model.CurrencyProperty.ToMoney() %>

これは明らかにビューに関連する(モデルに関連しない)問題であるため、可能な限りビューに保持するようにします。これは基本的に10進数の拡張メソッドに移動しますが、使用法はビューにあります。HtmlHelper拡張機能を実行することもできます。

public static string FormatMoney( this HtmlHelper helper, decimal amount )
{
    return string.Format( "{0:c}", amount );
}


<%= Html.FormatMoney( Model.CurrencyProperty ) %>

あなたがそのスタイルがもっと好きなら。これはHtmlHelper拡張機能であるため、ビューに多少関連しています。

于 2009-12-31T20:44:37.830 に答える
2

カスタム 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
   }
}
于 2010-01-05T14:08:24.677 に答える