2009-12-31 14 views
6

जिस परियोजना पर मैं काम कर रहा हूं उसके पास डोमेन मॉडल में बड़ी संख्या में मुद्रा गुण हैं और मुझे इन्हें देखने के लिए प्रेषण के लिए $#,###.## के रूप में प्रारूपित करने की आवश्यकता है। मेरे पास अलग-अलग दृष्टिकोणों के रूप में विचार विचार हैं जिनका उपयोग किया जा सकता है। एक दृष्टिकोण दृश्य के अंदर स्पष्ट रूप से मान स्वरूपित करने के लिए, "Pattern 1" from Steve Michelotti में के रूप में हो सकता है:कस्टम प्रारूपण के साथ एएसपी.नेट एमवीसी व्यू मॉडेल मैपिंग

... लेकिन यह बहुत जल्दी DRY principle का उल्लंघन करने शुरू होता है।

पसंदीदा दृष्टिकोण डोमेनमोडेल और व्यूमोडेल के बीच मैपिंग के दौरान प्रारूपण करना प्रतीत होता है (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 पर वापस कैसे मैप करें? मैं एक कस्टम 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 के साथ स्पष्ट रूप से परिभाषित करने की आवश्यकता नहीं है क्योंकि गुणों का नाम समान है - क्या कोई बेहतर है इस मैपिंग को परिभाषित करने का तरीका? जैसा कि मैंने उल्लेख किया है, मुद्रा मूल्यों के साथ बहुत अच्छी संपत्तियां हैं जिन्हें इस फैशन में मैप करने की आवश्यकता होगी।

कहा जा रहा है - क्या एक तरीका है कि मैं इन मैपिंगों को विश्व स्तर पर कुछ नियमों को परिभाषित करके स्वचालित रूप से हल कर सकता हूं? 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.NET MVC in Action से:

पहले तो हम सीधे देखने के लिए इस सरल वस्तु है, लेकिन दिनांक समय पारित करने के लिए परीक्षा हो सकती है? गुण [मॉडल में] समस्याएं पैदा करेगा। उदाहरण के लिए, हमें उनके लिए स्वरूपण जैसे ToShortDateString() या ToString() चुनने की आवश्यकता है। व्यू को को से स्क्रीन को रखने के लिए शून्य होने पर स्क्रीन को रखने के लिए जांच करने के लिए मजबूर किया जाएगा। दृश्य परीक्षण के लिए दृश्य कठिन हैं, इसलिए हम उन्हें जितना संभव हो पतला रखना चाहते हैं।क्योंकि व्यू का आउटपुट प्रतिक्रिया स्ट्रीम को पारित एक स्ट्रिंग है, हम केवल ऑब्जेक्ट्स का उपयोग करेंगे जो स्ट्रिंगफ़्रेंडली हैं; कि , ऑब्जेक्ट्स जो कभी भी विफल नहीं होंगे जब ToString() उन पर कॉल किया जाता है। कॉन्फ़्रेंसफ़ॉर्म दृश्य मॉडल ऑब्जेक्ट इसका उदाहरण है। 4.14 सूचीबद्ध करने में नोटिस कि सभी गुण स्ट्रिंग हैं। इस दृश्य मॉडल ऑब्जेक्ट को देखने के डेटा में रखे जाने से पहले हमारे पास दिनांक स्वरूपित होंगे। यह तरीका, दृश्य को ऑब्जेक्ट पर विचार करने की आवश्यकता नहीं है, और यह जानकारी को ठीक से प्रारूपित कर सकता है।

+0

<% = string.Format ("{0: c}", Model.CurrencyProperty)%> मुझे सुंदर लग रहा है। शायद मैं इसका उपयोग कर रहा हूं ... –

उत्तर

2

एक कस्टम TypeConverter आप जो खोज रहे हैं? यही वह है जो मैं उपयोग करता हूं और यह तेज़ और सरल है।

ViewModel : 
    [DisplayFormat(DataFormatString = "{0:c}", ApplyFormatInEditMode = true)] 
    public decimal CurrencyProperty { get; set; } 


View : 
    @Html.DisplayFor(m => m.CurrencyProperty) 
+0

उत्तर जिमी के लिए धन्यवाद। मैंने TypeConverter का उपयोग करने पर ध्यान दिया है, लेकिन मेरे मामले में जो समस्या मिली है वह यह है कि यह सभी * मैपिंग्स को दशमलव से स्ट्रिंग पर लागू हो जाएगी। दुर्भाग्यवश, केवल कुछ दशमलव गुण मुद्रा हैं। मैंने शायद दशमलव के चारों ओर एक रैपर बनाने का विचार किया - (वर्ग मुद्राडिसीमल: दशमलव), लेकिन फिर मैं टाइप और स्ट्रिंग के बीच आसानी से निहित कास्ट ऑपरेशंस जोड़ सकता था। जो मैं वास्तव में करना चाहता हूं वह टाइपकॉन्टर जैसा कुछ है जो संपत्ति विशेषताओं की जांच कर सकता है - शायद मैं इसे कभी-कभी लिखने पर विचार करूंगा यदि यह अस्तित्व में नहीं है। –

6

क्या आपने पैसे प्रारूपित करने के लिए एक विस्तार विधि का उपयोग करने पर विचार किया है?

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


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

चूंकि यह स्पष्ट रूप से एक दृश्य से संबंधित नहीं है (मॉडल से संबंधित) मुद्दा है, मैं अगर संभव हो दृश्य में रखने की कोशिश होगी। यह मूल रूप से इसे दशमलव पर एक विस्तार विधि में ले जाता है, लेकिन उपयोग दृश्य में है। आप एक एचटीएमएलहेल्पर एक्सटेंशन भी कर सकते हैं:

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


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

यदि आपको वह शैली बेहतर पसंद है। यह कुछ हद तक अधिक संबंधित है क्योंकि यह एक HTMLHelper एक्सटेंशन है।

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

हां, वे निश्चित रूप से स्ट्रिंग करने से अधिक समझ में आते हैं। फर्मेट() हर बार व्यू में। मुझे जिस समस्या का सामना करना पड़ रहा है वह यह है कि व्यूमोडेल अक्सर क्लाइंट को जावास्क्रिप्ट खपत के लिए प्रस्तुत किया जाएगा - ala http://www.trycatchfail.com/blog/post/2009/12/22/Exposing-the-View-Model- जावास्क्रिप्ट-इन-एएसपीएनईटी-एमवीसी.एएसपीएक्स या AJAX अनुरोधों के दौरान। इन मामलों में, मुझे क्लाइंट लेयर पर स्वरूपण करने की आवश्यकता होगी, जो वांछनीय से कम है - वास्तव में, मुझे लगता है कि मैं एक परत में अलग सभी स्वरूपण/पार्सिंग चिंताओं को अलग करने के लिए अतिरिक्त प्रयासों का एक गुच्छा हालांकि जाऊंगा । –

+1

यह भी मेरे लिए परेशान है कि एमवीसी के कस्टम मॉडल बाध्यकारी के माध्यम से आने वाले अनुरोधों को पार्स करने के लिए एक मजबूत तंत्र है, लेकिन दृश्य प्रतिपादन के दौरान प्रारूपण के लिए एक ही तरह का अनुभव प्रदान नहीं करता है। –

+0

मुझे प्रारूप या क्लाइंट स्वरूपण निर्णय लेने के लिए कोई समस्या नहीं है। आम तौर पर मैं नियंत्रक या मॉडल पर डेटा का प्रतिनिधित्व करने का विकल्प चुनता हूं - जो कि चिंताओं के सिद्धांत को अलग करने का उल्लंघन करता है। क्या होगा यदि अलग-अलग ग्राहक/विचार (उदाहरण के लिए मोबाइल बनाम वेब) इसे विभिन्न तरीकों से प्रस्तुत करना चाहते हैं? – tvanfosson

3

आप अपने ViewModel पर एक DisplayFormat डालने पर विचार किया है:

Mapper.CreateMap<string, decimal>().ConvertUsing<MoneyToDecimalConverter>(); 

तब कनवर्टर बनाएँ:

संबंधित मुद्दे