Autofac PropertiesAutowired - 是否可以忽略一个或多个属性?

时间:2014-02-06 15:11:18

标签: autofac

尽管通过构造函数传递依赖关系的建议我发现,拥有无参数构造函数然后自动装配所有属性的开发成本显着降低,并使应用程序更容易开发和维护。但有时(例如在视图模型上)我有一个在容器中注册的属性,但我不想在构造中填充(例如绑定到容器的选定项)。

有没有办法告诉容器在自动装配其余属性时忽略某些属性?

目前我只是重置了在激活的事件中标有属性的属性a la:

public static IRegistrationBuilder<TLimit, ScanningActivatorData, TRegistrationStyle> 
    PropertiesAutowiredExtended<TLimit, TRegistrationStyle>(
    this IRegistrationBuilder<TLimit, ScanningActivatorData, TRegistrationStyle> builder)
{
    builder.ActivatorData.ConfigurationActions.Add(
        (type, innerBuilder) =>
        {
            var parameter = Expression.Parameter(typeof(object));
            var cast = Expression.Convert(parameter, type);
            var assignments = type.GetProperties()
                .Where(candidate => candidate.HasAttribute<NotAutowiredAttribute>())
                .Select(property => new { Property = property, Expression = Expression.Property(cast, property) })
                .Select(data => Expression.Assign(data.Expression, Expression.Default(data.Property.PropertyType)))
                .Cast<Expression>()
                .ToArray();

            if (assignments.Any())
            {
                var @action = Expression
                    .Lambda<Action<object>>(Expression.Block(assignments), parameter)
                    .Compile();

                innerBuilder.OnActivated(e =>
                {
                    e.Context.InjectUnsetProperties(e.Instance);
                    @action(e.Instance);
                });
            }
            else
            {
                innerBuilder.OnActivated(e => e.Context.InjectUnsetProperties(e.Instance));
            }
        });

    return builder;
}

有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

不确定这是一个更好的,但你可以从另一边,通过WithProperty语法只注册所需的属性。优点是Autofac无法解决不必要的服务。这是一个有效的例子:

public class MyClass
{
    public MyDependency MyDependency { get; set; }
    public MyDependency MyExcludeDependency { get; set; }
}
public class MyDependency {}

public class Program
{
    public static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MyDependency>();
        builder.RegisterType<MyClass>().WithPropertiesAutowiredExcept("MyExcludeDependency");

        using (var container = builder.Build())
        {
            var myClass = container.Resolve<MyClass>();

            Console.WriteLine(myClass.MyDependency == null);
            Console.WriteLine(myClass.MyExcludeDependency == null);
        }
    }
}

public static class PropertiesAutowiredExtensions
{
    // Extension that registers only needed properties
    // Filters by property name for simplicity
    public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TRegistrationStyle>
        WithPropertiesAutowiredExcept<TLimit, TReflectionActivatorData, TRegistrationStyle>(
        this IRegistrationBuilder<TLimit, TReflectionActivatorData, TRegistrationStyle> registrationBuilder,
        params string[] propertiesNames)
        where TReflectionActivatorData : ReflectionActivatorData
    {
        var type = ((IServiceWithType)registrationBuilder.RegistrationData.Services.Single()).ServiceType;

        foreach (var property in type
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(pi => pi.CanWrite && !propertiesNames.Contains(pi.Name)))
        {
            // There's no additional checks like in PropertiesAutowired for simplicity
            // You can add them from Autofac.Core.Activators.Reflection.AutowiringPropertyInjector.InjectProperties

            var localProperty = property;
            registrationBuilder.WithProperty(
                new ResolvedParameter(
                    (pi, c) =>
                        {
                            PropertyInfo prop;
                            return pi.TryGetDeclaringProperty(out prop) &&
                                   prop.Name == localProperty.Name;
                        },
                    (pi, c) => c.Resolve(localProperty.PropertyType)));
        }

        return registrationBuilder;
    }

    // From Autofac.Util.ReflectionExtensions
    public static bool TryGetDeclaringProperty(this ParameterInfo pi, out PropertyInfo prop)
    {
        var mi = pi.Member as MethodInfo;
        if (mi != null && mi.IsSpecialName && mi.Name.StartsWith("set_", StringComparison.Ordinal)
            && mi.DeclaringType != null)
        {
            prop = mi.DeclaringType.GetProperty(mi.Name.Substring(4));
            return true;
        }

        prop = null;
        return false;
    }
}