如果没有适当地注入已批准的类型,则添加Autofac扩展以抛出异常

时间:2015-05-12 21:36:24

标签: c# dependency-injection autofac

我正在尝试创建一个Autofac扩展方法,该方法在为非批准对象类型的对象创建注册类型时会抛出异常。

public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> OnlyForInheritorsOf<TLimit, TReflectionActivatorData, TStyle>(
    this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration, Type baseType)
where TReflectionActivatorData : ReflectionActivatorData
{
    return registration.OnActivated(e =>
    {
        if (!baseType.IsInstanceOfType(??? instance of target/receiver ???))
            throw new InvalidOperationException("Expected to implement");
    });
}

e.Instance指向刚刚创建/激活的对象。但是,我需要有关我们将要提供的对象的信息。我怎样才能获得这些信息?

1 个答案:

答案 0 :(得分:0)

如果您需要指定某些注册需要特定类型而其他需要另外注册,您应该查看Named and Keyed Services

例如,如果IFoo有两个实现,则可以像这样注册它们。

builder.RegisterType<Foo1>().Named<IFoo>("foo1");
builder.RegisterType<Foo2>().Named<IFoo>("foo2");

如果Bar需要Foo1,您可以像这样注册Bar

builder.RegisterType<Bar>()
        .As<Bar>()
        .WithParameter((pi, c) => pi.ParameterType == typeof(IFoo), 
                       (pi, c) => c.ResolveNamed<IFoo>("foo1")); 

或者使用Bar构造函数上的属性指定密钥:

public class Bar
{
    public Bar([WithKey("foo1")]IFoo foo)
    { }
}

我不知道您的确切场景,可能需要一些代码重构,但这些更改可能比使用更复杂的场景更好。

顺便说一句,如果你真的想做你解释的事情。我编写了一些使用自定义ParameterModule的代码,如果调用者不是注册所需的类型,则会抛出异常。

public static class RegistrationExtensions
{
    public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> OnlyForInheritorsOf<TLimit, TReflectionActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration, Type baseType)
            where TReflectionActivatorData : ReflectionActivatorData
    {
        registration.RegistrationData.Metadata.Add(RestrictingRegistrationModule.MetadataKey, baseType);
        return registration;
    }
}

public class RestrictingRegistrationModule : Autofac.Module
{
    internal const String MetadataKey = "RestrictedType";


    internal class RestrictedAutowiringParameter : Parameter
    {
        public RestrictedAutowiringParameter(RestrictingRegistrationModule restrictingRegistrationModule)
        {
            this._restrictingRegistrationModule = restrictingRegistrationModule;
        }


        private readonly RestrictingRegistrationModule _restrictingRegistrationModule;


        public override Boolean CanSupplyValue(ParameterInfo pi, IComponentContext context, out Func<Object> valueProvider)
        {
            if (pi == null)
            {
                throw new ArgumentNullException("pi");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            IInstanceLookup lookup = context as IInstanceLookup;
            if (lookup != null)
            {
                IComponentRegistration registration;
                if (context.ComponentRegistry.TryGetRegistration(new TypedService(pi.ParameterType), out registration))
                {
                    Type restrictedType;
                    if (this._restrictingRegistrationModule.RestrictedRegistrations.TryGetValue(registration, out restrictedType))
                    {
                        Type callerType = lookup.ComponentRegistration.Activator.LimitType;
                        if (!(callerType == restrictedType || callerType.IsSubclassOf(restrictedType)))
                        {
                            throw new Exception(String.Format("Registration {0} is not compatible for type {1}", registration, callerType));
                        }
                    }

                    valueProvider = (() => context.ResolveComponent(registration, Enumerable.Empty<Parameter>()));
                    return true;
                }
            }

            valueProvider = null;
            return false;
        }
    }


    public RestrictingRegistrationModule()
    {
        this._restrictedRegistrations = new Dictionary<IComponentRegistration, Type>();
    }


    private readonly Dictionary<IComponentRegistration, Type> _restrictedRegistrations;


    public Dictionary<IComponentRegistration, Type> RestrictedRegistrations
    {
        get
        {
            return this._restrictedRegistrations;
        }
    }


    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        base.AttachToComponentRegistration(componentRegistry, registration);

        Object value;
        if (registration.Metadata.TryGetValue(RestrictingRegistrationModule.MetadataKey, out value))
        {
            this._restrictedRegistrations.Add(registration, (Type)value);
        }

        registration.Preparing += (sender, e) =>
        {
            e.Parameters = e.Parameters.Concat(new Parameter[] { new RestrictedAutowiringParameter(this) });
        };
    }

}

你可以使用这样的代码:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new RestrictingRegistrationModule());
builder.RegisterType<Bar>().As<Bar>();
builder.RegisterType<Bar2>().As<Bar2>();
builder.RegisterType<Foo>().As<IFoo>().OnlyForInheritorsOf(typeof(Bar));

IContainer container = builder.Build();

Bar bar = container.Resolve<Bar>();
Bar2 bar2 = container.Resolve<Bar2>(); // will throw

我没有对这段代码进行过深入测试,只是为了娱乐和教育目的,谨慎使用!