2009-01-23 8 views
8

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

तो ऐसा लगता है कि एकता में [निर्भरता] विशेषता का उपयोग केवल सार्वजनिक गुणों के लिए काम करता है। मुझे लगता है कि आंतरिक और निजी प्रोप एकता असेंबली के लिए दृश्यमान नहीं होंगे, लेकिन वास्तव में गंदे लगता है कि कभी भी किसी को भी सेट करने या सक्षम करने में सक्षम होना चाहिए, एकता के अलावा ।

क्या एकता को आंतरिक या निजी संपत्तियों को सेट करने का कोई तरीका है?

यहां यूनिट परीक्षण है जिसे मैं पास देखना चाहता हूं। वर्तमान में केवल सार्वजनिक प्रोप परीक्षण में सफ़ल:

[TestFixture] 
public class UnityFixture 
{ 
    [Test] 
    public void UnityCanSetPublicDependency() 
    { 
     UnityContainer container = new UnityContainer(); 
     container.RegisterType<HasPublicDep, HasPublicDep>(); 
     container.RegisterType<TheDep, TheDep>(); 

     var i = container.Resolve<HasPublicDep>(); 
     Assert.IsNotNull(i); 
     Assert.IsNotNull(i.dep); 
    } 

    [Test] 
    public void UnityCanSetInternalDependency() 
    { 
     UnityContainer container = new UnityContainer(); 
     container.RegisterType<HasInternalDep, HasInternalDep>(); 
     container.RegisterType<TheDep, TheDep>(); 

     var i = container.Resolve<HasInternalDep>(); 
     Assert.IsNotNull(i); 
     Assert.IsNotNull(i.dep); 
    } 

    [Test] 
    public void UnityCanSetPrivateDependency() 
    { 
     UnityContainer container = new UnityContainer(); 
     container.RegisterType<HasPrivateDep, HasPrivateDep>(); 
     container.RegisterType<TheDep, TheDep>(); 

     var i = container.Resolve<HasPrivateDep>(); 
     Assert.IsNotNull(i); 
     Assert.IsNotNull(i.depExposed); 
    } 
} 

public class HasPublicDep 
{ 
    [Dependency] 
    public TheDep dep { get; set; } 
} 

public class HasInternalDep 
{ 
    [Dependency] 
    internal TheDep dep { get; set; } 
} 

public class HasPrivateDep 
{ 
    [Dependency] 
    private TheDep dep { get; set; } 

    public TheDep depExposed 
    { 
     get { return this.dep; } 
    } 
} 

public class TheDep 
{ 
} 

अपडेट किया गया:

मैं कॉल स्टैक देखा संपत्ति से पारित कर दिया स्थापित करने के लिए: तो की कोशिश में

UnityCanSetPublicDependency() 
--> Microsoft.Practices.Unity.dll 
--> Microsoft.Practices.ObjectBuilder2.dll 
--> HasPublicDep.TheDep.set() 

लिए कम से कम आंतरिक संस्करण काम करते हैं, मैंने इन्हें अपनी असेंबली के गुणों में जोड़ा:

[assembly: InternalsVisibleTo("Microsoft.Practices.Unity")] 
[assembly: InternalsVisibleTo("Microsoft.Practices.Unity.Configuration")] 
[assembly: InternalsVisibleTo("Microsoft.Practices.ObjectBuilder2")] 

हालांकि, कोई बदलाव नहीं। एकता/ऑब्जेक्टबिल्डर अभी भी आंतरिक संपत्ति सेट नहीं करेगा

+0

कस्टम एक्सटेंशन बनाकर एक समाधान मिला। नीचे मेरा जवाब देखें ... – CodingWithSpike

उत्तर

2

परावर्तक में चारों ओर घूमने के बाद, मैंने इसे समझ लिया। डिफ़ॉल्ट रूप से, कोड है कि निर्माता इंजेक्शन कॉल के लिए एक निर्माता पाता है:

ConstructorInfo[] constructors = typeToConstruct.GetConstructors() 
कोई BindingFlags साथ

, कि केवल सार्वजनिक कंस्ट्रक्टर्स की पहचान करेगा। कुछ प्रवंचना के साथ करने के लिए आप एक UnityContainerExtension कि डिफ़ॉल्ट कार्यान्वयन के रूप में सभी एक ही सामान करता है बना सकते हैं, लेकिन GetConstructors (करने के लिए कॉल को बदलने) (कॉपी/परावर्तक से पेस्ट के रूप में):

ConstructorInfo[] constructors = typeToConstruct..GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) 

फिर एक्सटेंशन जोड़ने एकता कंटेनर में। लागू extenstion कोड की ~ 100 लाइनें है, इसलिए मैंने इसे यहां पेस्ट नहीं किया। अगर कोई इसे चाहता है, तो मुझे बताएं ...

नया कार्य परीक्षण केस। ध्यान दें कि सभी एकता बनाई गई है कि कक्षाओं में अब आंतरिक हैं:

[TestFixture] 
public class UnityFixture 
{ 
    [Test] 
    public void UnityCanSetInternalDependency() 
    { 
     UnityContainer container = new UnityContainer(); 
     container.AddNewExtension<InternalConstructorInjectionExtension>(); 
     container.RegisterType<HasInternalDep, HasInternalDep>(); 
     container.RegisterType<TheDep, TheDep>(); 

     var i = container.Resolve<HasInternalDep>(); 
     Assert.IsNotNull(i); 
     Assert.IsNotNull(i.dep); 
    } 
} 


internal class HasInternalDep 
{ 
    internal HasInternalDep(TheDep dep) 
    { 
     this.dep = dep; 
    } 

    internal TheDep dep { get; set; } 
} 

internal class TheDep 
{ 
} 

मुझे यकीन है कि मैं गैर सरकारी संपत्तियों को हल करने भी ऐसा ही करने के लिए एक विस्तार कर सकते हैं, लेकिन है कि कोड एक बहुत अधिक जटिल :)

था
5

यदि संपत्ति केवल मिलती है, तो यह संपत्ति इंजेक्शन के बजाय contructor injection का उपयोग करने के लिए अधिक समझ में आता है।

यदि यूनिटी निजी या आंतरिक सदस्यों को सेट करने के लिए प्रतिबिंब का उपयोग करते हैं, तो यह कोड पहुंच सुरक्षा बाधाओं के अधीन होगा। विशेष रूप से, यह कम विश्वास वाले वातावरण में काम नहीं करेगा।

+0

सच है, शायद यह एक क्लीनर समाधान है। अभी भी नाराज है कि एकता/ओबी आंतरिक का उपयोग नहीं कर सकता है। ओह ठीक है ... धन्यवाद! – CodingWithSpike

0

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

नई इकाई परीक्षण:

[TestFixture] 
public class UnityFixture 
{ 
    [Test] 
    public void UnityCanSetInternalDependency() 
    { 
     UnityContainer container = new UnityContainer(); 
     container.RegisterType<HasInternalDep, HasInternalDep>(); 
     container.RegisterType<TheDep, TheDep>(); 

     var i = container.Resolve<HasInternalDep>(); 
     Assert.IsNotNull(i); 
     Assert.IsNotNull(i.dep); 
    } 
    } 

internal class HasInternalDep 
{ 
    internal HasInternalDep(TheDep dep) 
    { 
     this._Dep = dep; 
    } 

    private TheDep _Dep; 
     internal TheDep dep 
     { 
      get { return _Dep; } 
     } 
} 

internal class TheDep 
{ 
} 
} 

विधानसभा के साथ जिम्मेदार बताते हैं:

[assembly: InternalsVisibleTo("Microsoft.Practices.Unity")] 
[assembly: InternalsVisibleTo("Microsoft.Practices.Unity.Configuration")] 
[assembly: InternalsVisibleTo("Microsoft.Practices.ObjectBuilder2")] 

त्रुटि के साथ विफल:

The type HasInternalDep does not have an accessible constructor. 
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, String name) 

तो कुल मिलाकर ऐसा लगता है कि आप एकता उपयोग करना चाहते हैं , आपको मूल रूप से बस सबकुछ सार्वजनिक करने के लिए कंबल करना होगा। वास्तव में उपयोगिता/लाइब्रेरी के लिए बदसूरत। डीएल ...

5

एक और समाधान [InjectionMethod] का उपयोग उस विधि पर करना है जहां आप कक्षा में निर्भरता पारित करते हैं।

public class MyClass { 
private ILogger logger; 

[InjectionMethod] 
public void Init([Dependency] ILogger logger) 
{ 
    this.logger = logger; 

... आदि


और यह बुला:

container.BuildUp<MyClass>(instanceOfMyClass); 

जो एकता पर निर्भरता के साथ Init कॉल करेंगे।

फ्लॉप काफी समस्या का समाधान, मुझे पता है ... लेकिन

:-) जम्मू

+0

बहुत अच्छा, मुझे उस विशेषता के बारे में पता नहीं था। धन्यवाद! – CodingWithSpike

0

यह मेरी आंतरिक निर्माता इंजेक्टर एक्सटेंशन वर्ग है:

बिग संभावित मुद्दा: 99 इसका% एकता संस्करण 4.1.0.0 से .NET परावर्तक से यूनिटी कोड की प्रतिलिपि/पेस्ट है। एकता के नए संस्करण कार्यान्वयन को बदल सकते हैं और इस एक्सटेंशन को तोड़ सकते हैं, या फ्लेकी त्रुटियों का कारण बन सकते हैं। आपको चेतावनी दी गई है!

using System; 
using System.Collections.Generic; 
using System.Globalization; 
using System.Reflection; 
using Microsoft.Practices.ObjectBuilder2; 
using Microsoft.Practices.Unity; 
using Microsoft.Practices.Unity.ObjectBuilder; 
using Microsoft.Practices.Unity.Utility; 

namespace MyApp.Unity.Configuration 
{ 
    /// <summary> 
    /// This extension changes the behavior of Unity constructor injection to allow the use of non-public constructors. 
    /// By default, Unity/ObjectBuilder would call Type.GetConstructors() to get the constructors. With the default binding 
    /// flags, this only returns public constructors. 
    /// The code here is 99% copy/paste from Reflector's dissassembly of the default Unity/OB implementation. 
    /// My only change was to add binding flags to get all constructors, not just public ones. 
    /// For more info, see: Microsoft.Practices.Unity.ObjectBuilder.DefaultUnityConstructorSelectorPolicy 
    /// </summary> 
    public class InternalConstructorSelectorPolicy : IConstructorSelectorPolicy 
    { 
     protected IDependencyResolverPolicy CreateResolver(ParameterInfo param) 
     { 
      List<DependencyResolutionAttribute> list = new List<DependencyResolutionAttribute>(Sequence.OfType<DependencyResolutionAttribute>(param.GetCustomAttributes(false))); 
      if (list.Count > 0) 
      { 
       return list[0].CreateResolver(param.ParameterType); 
      } 
      return new NamedTypeDependencyResolverPolicy(param.ParameterType, null); 
     } 

     private SelectedConstructor CreateSelectedConstructor(IBuilderContext context, ConstructorInfo ctor) 
     { 
      SelectedConstructor constructor = new SelectedConstructor(ctor); 
      foreach (ParameterInfo info in ctor.GetParameters()) 
      { 
       string buildKey = Guid.NewGuid().ToString(); 
       IDependencyResolverPolicy policy = this.CreateResolver(info); 
       context.PersistentPolicies.Set<IDependencyResolverPolicy>(policy, buildKey); 
       DependencyResolverTrackerPolicy.TrackKey(context.PersistentPolicies, context.BuildKey, buildKey); 
       constructor.AddParameterKey(buildKey); 
      } 
      return constructor; 
     } 

     private ConstructorInfo FindInjectionConstructor(Type typeToConstruct) 
     { 
      ConstructorInfo[] infoArray = Array.FindAll<ConstructorInfo>(typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), delegate(ConstructorInfo ctor) 
      { 
       return ctor.IsDefined(typeof(InjectionConstructorAttribute), true); 
      }); 
      switch (infoArray.Length) 
      { 
       case 0: 
        return null; 

       case 1: 
        return infoArray[0]; 
      } 
      throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Resources.MultipleInjectionConstructors", new object[] { typeToConstruct.Name })); 
     } 

     private ConstructorInfo FindLongestConstructor(Type typeToConstruct) 
     { 
      ConstructorInfo[] constructors = typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 
      Array.Sort<ConstructorInfo>(constructors, new ConstructorLengthComparer()); 
      switch (constructors.Length) 
      { 
       case 0: 
        return null; 

       case 1: 
        return constructors[0]; 
      } 
      int length = constructors[0].GetParameters().Length; 
      if (constructors[1].GetParameters().Length == length) 
      { 
       throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Resources.AmbiguousInjectionConstructor", new object[] { typeToConstruct.Name, length })); 
      } 
      return constructors[0]; 
     } 

     public virtual SelectedConstructor SelectConstructor(IBuilderContext context) 
     { 
      Type typeToConstruct = BuildKey.GetType(context.BuildKey); 
      ConstructorInfo ctor = this.FindInjectionConstructor(typeToConstruct) ?? this.FindLongestConstructor(typeToConstruct); 
      if (ctor != null) 
      { 
       return this.CreateSelectedConstructor(context, ctor); 
      } 
      return null; 
     } 

     // Nested Types 
     private class ConstructorLengthComparer : IComparer<ConstructorInfo> 
     { 
      // Methods 
      public int Compare(ConstructorInfo x, ConstructorInfo y) 
      { 
       return (y.GetParameters().Length - x.GetParameters().Length); 
      } 
     } 
    } 

    /// <summary> 
    /// Registeres the InternalConstructorSelectorPolicy with the Unity container. 
    /// </summary> 
    public class InternalConstructorInjectionExtension : UnityContainerExtension 
    { 
     protected override void Initialize() 
     { 
      this.Context.Policies.SetDefault(typeof(IConstructorSelectorPolicy), new InternalConstructorSelectorPolicy()); 
     } 
    } 
} 
3

उद्यम लाइब्रेरी 5,0

के लिए अपडेट rally52rs हो सकता चेतावनी दी के रूप में, EntLib5.0 के उन्नयन उसके कार्यान्वयन टूट जाता है। रैली के समान दृष्टिकोण का उपयोग करके, मैंने नए कोड बेस पर प्रतिबिंबित किया और आंतरिक नियंत्रक चयनकर्ता नीति के निम्नलिखित 5.0 संगत संस्करण का काम किया।

ध्यान दें कि मेरा संस्करण विशेष रूप से FindLongestConstructor विधि में आंतरिक रचनाकारों तक सीमित है। इस बिंदु पर मेरा कोड रैली के से कार्यात्मक रूप से अलग है।

public class InternalConstructorSelectorPolicy : IConstructorSelectorPolicy, IBuilderPolicy 
{ 
    private IDependencyResolverPolicy CreateResolver(ParameterInfo parameter) 
    { 
     List<DependencyResolutionAttribute> attrs = parameter.GetCustomAttributes(false).OfType<DependencyResolutionAttribute>().ToList<DependencyResolutionAttribute>(); 
     if (attrs.Count > 0) 
     { 
      return attrs[0].CreateResolver(parameter.ParameterType); 
     } 
     return new NamedTypeDependencyResolverPolicy(parameter.ParameterType, null); 
    } 

    private SelectedConstructor CreateSelectedConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination, ConstructorInfo ctor) 
    { 
     SelectedConstructor result = new SelectedConstructor(ctor); 
     foreach (ParameterInfo param in ctor.GetParameters()) 
     { 
      string key = Guid.NewGuid().ToString(); 
      IDependencyResolverPolicy policy = this.CreateResolver(param); 
      resolverPolicyDestination.Set<IDependencyResolverPolicy>(policy, key); 
      DependencyResolverTrackerPolicy.TrackKey(resolverPolicyDestination, context.BuildKey, key); 
      result.AddParameterKey(key); 
     } 
     return result; 
    } 

    private static ConstructorInfo FindInjectionConstructor(Type typeToConstruct) 
    { 
     ConstructorInfo[] injectionConstructors = typeToConstruct 
      .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) 
      .Where<ConstructorInfo>(delegate(ConstructorInfo ctor) 
     { 
      return ctor.IsDefined(typeof(InjectionConstructorAttribute), true); 
     }).ToArray<ConstructorInfo>(); 
     switch (injectionConstructors.Length) 
     { 
      case 0: 
       return null; 

      case 1: 
       return injectionConstructors[0]; 
     } 
     throw new InvalidOperationException(string.Format("Multiple constructors found for {0}" , typeToConstruct.Name)); 
    } 

    private static ConstructorInfo FindLongestConstructor(Type typeToConstruct) 
    { 
     var constructors = 
      Array.FindAll(
       typeToConstruct.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), 
       ctor => !ctor.IsFamily && !ctor.IsPrivate); //Filter out protected and private constructors 

     Array.Sort<ConstructorInfo>(constructors, new ConstructorLengthComparer()); 
     switch (constructors.Length) 
     { 
      case 0: 
       return null; 

      case 1: 
       return constructors[0]; 
     } 
     int paramLength = constructors[0].GetParameters().Length; 
     if (constructors[1].GetParameters().Length == paramLength) 
     { 
      throw new InvalidOperationException(string.Format("Ambiguous constructor found for {0}", typeToConstruct.Name)); 
     } 
     return constructors[0]; 
    } 

    public SelectedConstructor SelectConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination) 
    { 
     Type typeToConstruct = context.BuildKey.Type; 
     ConstructorInfo ctor = FindInjectionConstructor(typeToConstruct) ?? FindLongestConstructor(typeToConstruct); 
     if (ctor != null) 
     { 
      return this.CreateSelectedConstructor(context, resolverPolicyDestination, ctor); 
     } 
     return null; 
    } 

    // Nested Types 
    private class ConstructorLengthComparer : IComparer<ConstructorInfo> 
    { 
     // Methods 
     public int Compare(ConstructorInfo x, ConstructorInfo y) 
     { 
      return (y.GetParameters().Length - x.GetParameters().Length); 
     } 
    } 
} 
+0

मैं अभी तक EntLib5 में स्थानांतरित नहीं हुआ हूं, इसलिए जब भी मैं करता हूं तो यह मुझे सिरदर्द बचाएगा ... कोड के लिए धन्यवाद! – CodingWithSpike

1

@ rally25rs, हालांकि पद दो से अधिक वर्ष यह अभी भी उच्च स्थान है (विचारों/गूगल आदि) इसलिए मैंने सोचा कि मैं अपने 2 सेंट जोड़ना होगा .. मैं एक ही समस्या मिला है और अंततः इस समाधान को चुना: UnityContainer and internal constructor। यह एक टिप्पणी के रूप में है लेकिन मैं अभी तक टिप्पणियां पोस्ट नहीं कर सकता हूं।

आपने शायद इसे देखा है और इसे पहले से ही जानते हैं, फिर भी यह किसी और के देखने के लिए उपयोग किया जा सकता है: InternalsVisibleTo() विशेषता कभी काम नहीं करनी चाहिए - ऐसा इसलिए है क्योंकि एकता सीधे आपकी कक्षाओं को नहीं बुला रही है। इसके बजाय, यह प्रतिबिंब का उपयोग कर रहा है और Type का निरीक्षण कर रहा है। बेशक, Type वहां मौजूद विशेषता के परिणामस्वरूप नहीं बदला गया है। प्राप्त करने वाले पक्ष पर आंतरिक दृश्यों के लाभों का आनंद लेने के लिए, आपको स्पष्ट रूप से आंतरिक c'tor (या संपत्ति) को कॉल करना होगा।

2

प्रश्न स्वयं एक गलतफहमी प्रतीत होता है।

कोर बयान के बारे में:

a bunch of public properties that you never want anyone to set or be able to set, other than Unity.

आप उन्हें इकाई परीक्षण में सेट करना चाहते हैं, या फिर आप कैसे निर्भरता mocks पारित होगा? भले ही आपके पास यूनिट परीक्षण न हों, भले ही निर्भरताएं हों कि कुछ भी नहीं (एकता के कुछ जादू को छोड़कर) सेट कर सकते हैं। क्या आप चाहते हैं कि आपका कोड समर्थन टूल पर इतना निर्भर करे?

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

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

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