统一框架DependencyAttribute只适用于公共属性?
-
19-08-2019 - |
题
我试图清理一些东西前往的在我的代码,并在不经意间打破了统一的依赖注入。过了一会儿,我意识到,我标志着我真的不希望暴露我的DLL外内一些公共属性。然后,我开始变得异常。
因此看来,使用在Unity [依赖]属性仅适用于公共属性。我想这是有道理的,因为内部和私人道具不会是要统一装配可见,但的感觉很肮脏的有一堆,你的的公共属性从不的想要的任何人设置或能够设置,除了统一。
有没有办法让统一设定的内部或私有财产吗?
下面是单元测试我想看到通。目前,只有公共丙测试通过:
[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")]
然而,没有任何变化。团结/ ObjectBuilder的仍然将不设置内部属性
解决方案 5
在反射器闲逛的LOF后好,我想这出。 通过默认情况下,发现一个构造用于构造子注入调用的代码:
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
{
}
我敢肯定,我可以提出延期做同样的解决非公有制性质,但这些代码是一个复杂多了:)
其他提示
另一种解决方案是使用[InjectionMethod]你在哪里通过依赖到类的方法。
public class MyClass {
private ILogger logger;
[InjectionMethod]
public void Init([Dependency] ILogger logger)
{
this.logger = logger;
...等
和调用它:
container.BuildUp<MyClass>(instanceOfMyClass);
,它将调用初始化与从统一的依赖性。
didn't相当解决的问题,我知道......但
:-)Ĵ
如果该属性是让只,这让使用构造器更有意义注射而非属性注射。
如果统一确实使用反射来设置私人或内部构件,它会受到代码访问安全约束。具体而言,它不会在低信任的环境中工作。
这个问题本身似乎是一种误解。
关于核心语句:
一堆,你永远不希望任何人设定或成为公共财产 能够设置,除了统一。
您想设置它们在单元测试中,或者是怎么回事,你会通过依赖嘲弄? 即使你没有单元测试,这是一个奇怪的主意,有依赖性,没有什么(除了统一一些魔法)可以设置。你想让你的代码依赖这么多的支持工具?
此外,具有公共属性是不是在所有问题,因为您的代码必须依赖于接口,而不是对实施方式(固体原则之一)。如果不遵循这样的原则 - 没有理由让你使用统一。当然,你也不会在界面中声明依赖,所以消费类不知道它们。
您已经被告知,这是更好地使用构造函数注射,但注射财产也有它的美丽。它允许添加新的依赖较少修饰(特别是,可以避免在所有改变现有的单元测试,只添加新的)。
<强>更新企业库5.0 强>
作为警告rally52rs可能发生,升级到EntLib5.0打破他的执行。使用相同的方法作为拉力那样,我反映在新的代码库并进行后处理的InternalConstructorSelectorPolicy以下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);
}
}
}
@ rally25rs,虽然职位是两年多岁,它仍然稳居高位(视图/谷歌等),所以我想我会加入我的2美分..我有同样的问题,并最终选择了这个解决方案: UnityContainer和内部构造。 这意味着作为一个评论,但我不能发表评论,只是还没有。
您可能已经看到和已经知道了,它仍然可能使用的其他任何人观看:该InternalsVisibleTo()
属性不应该工作 - 这是因为统一是不直接调用你的类。相反,它使用反射和检查Type
。当然,Type
不改变作为属性在那里的结果。 “享受”在接收端可见等内部的利益,你必须显式调用内部c'tor(或财产)。
,我更改为使用构造器注入,这也为公共类的工作。但是其根源问题依然存在,在任何你想分配或已被统一分配必须是公开的。这包括类本身。
新的单元测试:
[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)
所以整体看来,如果你想使用统一,你基本上只需要毯子标记一切公众。真难看用于多用途/库的.dll ...
这是我的内部构造注射器Extension类:
<强>大潜在的问题:强>的这99%是从.NET反射器的统一码的复制/粘贴,从统一版本4.1.0.0。团结的新版本可能会改变执行和打破这种扩展,或导致古怪的错误。你警告!
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());
}
}
}