Properties 使用内部setter的属性注入

Properties 使用内部setter的属性注入,properties,autofac,internal,Properties,Autofac,Internal,我有一个正在修改的现有应用程序,以使用Autofac属性注入。似乎无论使用哪种方法将类型注册到属性,属性总是空的,除非它们有公共设置器。对于其他IoC容器(例如Structuremap),可以使用程序集上的InternalsVisibleTo属性来确定setter internal的范围并使其可用。限制客户端修改分配似乎很好 Autofac是否可以实现这一点?或者在使用属性注入来确保分配安全时,是否有其他方法 我已经尝试将反射与属性autowired()一起使用,并从我的WebApi Globa

我有一个正在修改的现有应用程序,以使用Autofac属性注入。似乎无论使用哪种方法将类型注册到属性,属性总是空的,除非它们有公共设置器。对于其他IoC容器(例如Structuremap),可以使用程序集上的
InternalsVisibleTo
属性来确定setter internal的范围并使其可用。限制客户端修改分配似乎很好

Autofac是否可以实现这一点?或者在使用属性注入来确保分配安全时,是否有其他方法

我已经尝试将反射与
属性autowired()
一起使用,并从我的WebApi Global.asax中解析
.WithParameter()
——指定要设置的特定参数作为内部设置器,但没有成功

[assembly: InternalsVisibleTo("MyWebAPI.dll")]
[assembly: InternalsVisibleTo("Autofac.dll")]
[assembly: InternalsVisibleTo("Autofac.Configuration.dll")]
namespace My.Namespace
{
    public class BaseContext
    {
        public MyPublicClass _dbHelper { get; internal set; }

        public BaseContext()
        {

        }

        protected string DbConnectionString
        {
            get
            {
                return _dbHelper.DbConn; //<-Always null unless setter is public
            }
        }
    }
}
[程序集:InternalsVisibleTo(“MyWebAPI.dll”)]
[程序集:InternalsVisibleTo(“Autofac.dll”)]
[程序集:InternalsVisibleTo(“Autofac.Configuration.dll”)]
名称空间My.namespace
{
公共类基类上下文
{
公共MyPublicClass dbHelper{get;内部集合;}
公共BaseContext()
{
}
受保护的字符串DbConnectionString
{
得到
{

return _dbHelper.DbConn;//您不能使用autofac注入
内部设置程序,因为
AutowiringPropertyInjector
类只查找公共属性(请参阅)

但是,
AutowiringPropertyInjector
中的逻辑非常简单,因此您可以创建自己的版本,对非公共属性进行注入:

公共静态类自动连线NonPublicPropertyInjector
{
公共静态属性(IComponentContext上下文,
对象实例,bool覆盖ESETVALUES)
{
if(上下文==null)
抛出新的ArgumentNullException(“上下文”);
if(实例==null)
抛出新的ArgumentNullException(“实例”);
弗雷奇(
PropertyInfo PropertyInfo in
//为非公共属性添加了BindingFlags.NonPublic标志
instance.GetType().GetProperties(BindingFlags.instance|
BindingFlags.Public|
BindingFlags(非公开)
{
类型propertyType=propertyInfo.propertyType;
if((!propertyType.IsValueType | | propertyType.IsEnum)&&
(propertyInfo.GetIndexParameters().Length==0&&
context.IsRegistered(propertyType)))
{
//更改为GetAccessors(true)以返回非公共访问器
MethodInfo[]访问器=propertyInfo.GetAccessors(true);
如果((accessors.Length!=1 | |
!(访问者[0]。返回类型!=typeof(void)))&&
(overrideSetValues | |访问器。长度!=2||
propertyInfo.GetValue(实例,null)==null))
{
object obj=context.Resolve(propertyType);
propertyInfo.SetValue(实例,obj,null);
}
}
}
}
}
现在您可以在
OnActivated
事件中使用这个类

var builder=newcontainerbuilder();
RegisterType();
builder.RegisterType()
.on已激活(args=>
自动接线非公共属性注入器
.InjectProperties(args.Context,args.Instance,true));
但是,上面列出的解决方案现在会注入所有类型的属性,即使是私有和受保护的属性,因此您可能需要通过一些额外的检查来扩展它,以确保只注入您所期望的属性。

我使用的解决方案如下:

builder.RegisterType();
builder.RegisterType()
.on激活(CustomPropertiesHandler);
使用这样的处理程序:

//如果启用:Autofac.Core.IActivatedEventArgs
public void CustomPropertiesHandler(Autofac.Core.IActivatingEventArgs e)
{
var props=e.Instance.GetType()
.GetTypeInfo().DeclaredProperties//还包括带有“public set”的“private prop”
.Where(pi=>pi.CanWrite)//有一个set访问器。
//.Where(pi=>pi.SetMethod.IsPrivate)//set访问器是私有的
.Where(pi=>e.Context.IsRegistered(pi.PropertyType));//类型可解析
foreach(道具中的var道具)
prop.SetValue(e.Instance,e.Context.Resolve(prop.PropertyType),null);
}

由于IActivatingEventArgs和IActivatedEventArgs都有实例和上下文,您可能希望使用在CustomPropertiesHandler上使用这些参数的包装方法。

我们还可以将@nemesv实现作为扩展方法编写

public static class AutofacExtensions
{
    public static void InjectProperties(IComponentContext context, object instance, bool overrideSetValues)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }
        if (instance == null)
        {
            throw new ArgumentNullException(nameof(instance));
        }

        foreach (var propertyInfo in instance.GetType().GetProperties(BindingFlags.Instance |
                                                                      BindingFlags.Public |
                                                                      BindingFlags.NonPublic))
        {
            var propertyType = propertyInfo.PropertyType;

            if ((!propertyType.IsValueType || propertyType.IsEnum) && (propertyInfo.GetIndexParameters().Length == 0) && context.IsRegistered(propertyType))
            {
                var accessors = propertyInfo.GetAccessors(true);
                if (((accessors.Length != 1) ||
                     !(accessors[0].ReturnType != typeof(void))) &&
                    (overrideSetValues || (accessors.Length != 2) ||
                     (propertyInfo.GetValue(instance, null) == null)))
                {
                    var obj = context.Resolve(propertyType);
                    propertyInfo.SetValue(instance, obj, null);
                }
            }
        }
    }

    public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InjectPropertiesAsAutowired<TLimit, TActivatorData, TRegistrationStyle>(
        this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registration)
    {
        return registration.OnActivated(args => InjectProperties(args.Context, args.Instance, true));
    }
公共静态类AutofacExtensions
{
公共静态属性(IComponentContext上下文、对象实例、bool overridesetValue)
{
if(上下文==null)
{
抛出新ArgumentNullException(nameof(context));
}
if(实例==null)
{
抛出新ArgumentNullException(nameof(instance));
}
实例.GetType().GetProperties(BindingFlags.instance)中的foreach(var propertyInfo|
BindingFlags.Public|
BindingFlags(非公开)
{
var propertyType=propertyInfo.propertyType;
如果((!propertyType.IsValueType | | propertyType.IsEnum)&&(propertyInfo.GetIndexParameters().Length==0)&&context.IsRegistered(propertyType))
{
var accessors=propertyInfo.GetAccessors(true);
if(((accessors.Length!=1)||
!(访问器[0]。返回类型!=ty
protected override void Load(ContainerBuilder builder)
{
    builder.RegisterType<StartupConfiguration>().As<IStartupConfiguration>().AsSelf().InjectPropertiesAsAutowired().AsImplementedInterfaces().SingleInstance();
}
public virtual bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
   if (!propertyInfo.CanWrite || propertyInfo.SetMethod?.IsPublic != true)
   {
       return false;
   }
   ....
 }
public class AccessRightInvariantPropertySelector : DefaultPropertySelector
{
    public AccessRightInvariantPropertySelector(bool preserveSetValues) : base(preserveSetValues)
    { }

    public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
    {
        if (!propertyInfo.CanWrite)
        {
            return false;
        }

        if (!PreserveSetValues || !propertyInfo.CanRead)
        {
            return true;
        }
        try
        {
            return propertyInfo.GetValue(instance, null) == null;
        }
        catch
        {
            // Issue #799: If getting the property value throws an exception
            // then assume it's set and skip it.
            return false;
        }
    }
}
builder.RegisterType<AppService>()
      .AsImplementedInterfaces()
      .PropertiesAutowired(new AccessRightInvariantPropertySelector(true));
PM> Install-Package Autofac.Core.NonPublicProperty
builder.RegisterType<AppService>()
      .AsImplementedInterfaces()
      .AutoWireNonPublicProperties();