6

मैं सरल इंजेक्टर का उपयोग कर रहा हूं, लेकिन शायद मुझे जो चाहिए वह एक वैचारिक उत्तर है। एक आम तौर पर, एक वर्ग जो IApplicationSettings लागू करता है के लिए होता है एक निर्दिष्ट स्रोत से प्रत्येक क्षेत्र में हो रही, उदाहरण के लिएनिर्भरता इंजेक्शन का उपयोग कैसे करें, एकाधिक स्रोतों से कॉन्फ़िगरेशन प्राप्त करें?

public interface IApplicationSettings 
{ 
    bool EnableLogging { get; } 
    bool CopyLocal { get; } 
    string ServerName { get; } 
} 

फिर,:

यहाँ सौदा है, मैं अपने आवेदन सेटिंग्स के साथ एक अंतरफलक है लगता है

public class AppConfigSettings : IApplicationSettings 
{ 
    private bool? enableLogging; 
    public bool EnableLogging 
    { 
     get 
     { 
      if (enableLogging == null) 
      { 
       enableLogging = Convert.ToBoolean(ConfigurationManager.AppSettings["EnableLogging"]; 
      } 
      return enableLogging; 
     } 
    } 
    ... 
} 

हाउवर! मान लें कि मैं app.config से EnableLogging, डेटाबेस से CopyLocal, और ServerName को अन्य कार्यान्वयन से प्राप्त करना चाहता हूं जो वर्तमान कंप्यूटर नाम प्राप्त करता है। मैं 9 एन्क्रिप्शन बनाने के बिना अपने एप कॉन्फ़िगरेशन को मिश्रित करने में सक्षम होना चाहता हूं, प्रत्येक संयोजन के लिए एक।

मुझे लगता है कि मैं किसी भी पैरामीटर को पार नहीं कर सकता क्योंकि इंटरफेस इंजेक्टर (कंटेनर) द्वारा हल किया जाता है।

मैं इस के बारे में सोचा, शुरू में:

public interface IApplicationSettings<TEnableLogging,TCopyLocal,TServerName> 
where TEnableLogging : IGetValue<bool> 
where TCopyLocal : IGetValue<bool> 
where TServerName : IGetValue<string> 
{ 
    TEnableLogging EnableLog{get;} 
    TCopyLocal CopyLocal{get;} 
    TServerName ServerName{get;} 
} 

public class ApplicationSettings<TEnableLogging,TCopyLocal,TServerName> 
{ 
    private bool? enableLogging; 
    public bool EnableLogging 
    { 
     get 
     { 
      if (enableLogging == null) 
      { 
       enableLogging = Container.GetInstance<TEnableLogging>().Value 
      } 
      return enableLogging; 
     } 
    } 
} 

हालांकि, इस के साथ मैं एक मुख्य समस्या है: मुझे कैसे पता TEnableLogging का एक उदाहरण (जो एक IGetValue<bool> है) बनाने के लिए कैसे करते हैं? ओह, मान लें कि IGetValue<bool> एक इंटरफ़ेस है जिसमें एक वैल्यू प्रॉपर्टी है, जिसे कंक्रीट क्लास द्वारा कार्यान्वित किया जाएगा। लेकिन कंक्रीट क्लास को कुछ विनिर्देशों की आवश्यकता हो सकती है (जैसे app.config में कुंजी का नाम क्या है) या नहीं (मैं बस हमेशा सच करना चाहता हूं)।

मैं अपेक्षाकृत इंजेक्शन के लिए अपेक्षाकृत नया हूं, इसलिए शायद मैं गलत तरीके से सोच रहा हूं। क्या किसी को यह पूरा करने के बारे में कोई विचार है?

(आप एक और डि लाइब्रेरी का उपयोग कर का जवाब हो सकता है, मुझे कोई आपत्ति नहीं होगा। मुझे लगता है कि मैं सिर्फ यह की अवधारणा को आकर्षित करने की जरूरत है।)

उत्तर

11

आप निश्चित रूप से यहाँ गलत तरीके से जा रहे हैं।

कुछ साल पहले मैंने एक ऐसा एप्लिकेशन बनाया जिसमें आपके IApplicationSettings की तरह एक इंटरफ़ेस शामिल था। मेरा मानना ​​है कि मैंने इसे IApplicationConfiguration नाम दिया है, लेकिन इसमें सभी एप्लिकेशन के कॉन्फ़िगरेशन मान भी शामिल हैं।

हालांकि यह मुझे पहले से ही मेरे आवेदन को टेस्ट करने योग्य बनाने में मदद करता है, कुछ समय बाद डिजाइन रास्ते में आने लगा। उस इंटरफ़ेस पर बहुत से कार्यान्वयन निर्भर थे, लेकिन यह बहुत कुछ बदल रहा था और इसके साथ कार्यान्वयन, और परीक्षण संस्करण।

बस आपके जैसे मैंने कुछ आलसी लोडिंग लागू की, लेकिन यह एक भयानक नीचे की ओर था। जब कॉन्फ़िगरेशन मानों में से एक गायब था, तो मुझे केवल यह पता चला कि यह तब हुआ जब मूल्य पहली बार बुलाया गया था। इसके परिणामस्वरूप एक कॉन्फ़िगरेशन हुआ जो hard to verify था।

मुझे समस्या का मूल क्या था यह दोबारा दोबारा दोहराया गया। बड़े इंटरफेस एक समस्या है। मेरा IApplicationConfiguration वर्ग Interface Segregation Principle का उल्लंघन कर रहा था और परिणाम खराब रखरखाव था।

अंत में मुझे पता चला कि यह इंटरफ़ेस पूरी तरह से बेकार था।आईएसपी का उल्लंघन करने के अलावा, उन कॉन्फ़िगरेशन मानों ने एक कार्यान्वयन विस्तार का वर्णन किया और एक आवेदन व्यापक व्याकरण बनाने के बजाय, प्रत्येक कार्यान्वयन को सीधे कॉन्फ़िगरेशन मूल्य के साथ आपूर्ति करने के लिए बेहतर था।

जब आप ऐसा करते हैं, तो सबसे आसान बात यह है कि उस कॉन्फ़िगरेशन मान को संपत्ति मान के रूप में पास करना है।

var enableLogging = 
    Convert.ToBoolean(ConfigurationManager.AppSettings["EnableLogging"]); 

container.RegisterInitializer<Logger>(logger => 
{ 
    logger.EnableLogging = enableLogging; 
}); 

जब ऐसा कर रही है, enableLogging मूल्य सिर्फ विन्यास फाइल से एक बार पढ़ा जाता है और आवेदन स्टार्टअप के दौरान इसलिए किया जाता है: सरल इंजेक्टर साथ आप इस के लिए RegisterInitializer विधि का उपयोग कर सकते हैं। यह तेज़ बनाता है और जब मूल्य गुम हो जाता है तो एप्लिकेशन स्टार्टअप पर विफल हो जाता है।

किसी कारण से आप (उदाहरण के लिए डेटाबेस से) पढ़ने में देरी करने की जरूरत है, तो आप Lazy<T> उपयोग कर सकते हैं: इसके बजाय गुणों का उपयोग करके मान पास करने का

Lazy<bool> copyLocal = new Lazy<bool>(() => 
    container.GetInstance<IDatabaseManager>().RunQuery(CopyLocalQuery)); 

container.RegisterInitializer<FileCopier>(copier => 
{ 
    copier.CopyLocal = copyLocal.Value; 
}); 

, आप भी निर्माता तर्क का उपयोग कर सकते हैं, लेकिन यह हासिल करने के लिए थोड़ा मुश्किल है। कुछ विचारों के लिए look at this article लें।

यदि आप स्वयं को कई रजिस्ट्रेशन के लिए एक ही कॉन्फ़िगरेशन मान पंजीकृत करते हैं, तो संभवतः आप एक अमूर्तता खो रहे हैं। इस पर एक नज़र डालें:

container.RegisterInitializer<UserRepository>(rep => { 
    rep.ConnectionString = connectionString; }); 
container.RegisterInitializer<OrderRepository>(rep => { 
    rep.ConnectionString = connectionString; }); 
container.RegisterInitializer<CustomerRepository>(rep => { 
    rep.ConnectionString = connectionString; }); 
container.RegisterInitializer<DocumentRepository>(rep => { 
    rep.ConnectionString = connectionString; }); 

इस मामले में आप शायद एक IDatabaseFactory या IDatabaseManager अमूर्त याद कर रहे हैं, और आप कुछ इस तरह करना चाहिए:

container.RegisterSingle<IDatabaseFactory>(new SqlDatabaseFactory(connectionString)); 
+1

धन्यवाद, उस अर्थ का एक बहुत बनाया! दिलचस्प है कि हम दोनों ने समान कदम उठाए और महसूस किया कि जिस तरह से यह हाहा होना चाहिए –

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