如何使用自动注册对单个图书馆进行范围划分 [英] How To Scope Individual Libraries Using Auto Registration

查看:62
本文介绍了如何使用自动注册对单个图书馆进行范围划分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您要自动注册/发现依赖项,是否可以为一个,两个或x个特定程序集设置/更改依赖项生存期作用域范围选项(瞬态,单个,PerWebReqest等)?我下面有示例代码:

Is it possible to set/change the dependency lifetime scoping options (Transient, Single, PerWebReqest, etc...) for one, two or x number of specific assemblies if you're auto registering/discovering your dependencies? I have sample code below:

public class RegistrationPackage : IPackage
{
    public void RegisterServices(Container container)
    {
        var @namespace = "ConsoleLib";
    var assemblies = AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => a.FullName.StartsWith(@namespace))
        .Select(a => a).ToList();

    var names = assemblies.Select(a => a.FullName);

    Action<string> poormanslogger = Console.WriteLine;
    //poormanslogger = Trace.WriteLine;

    foreach (var name in names)
        poormanslogger("Located the following assemblies: " + name);

    // could combine these two and remove the if statement for the open generic registrations below
    //container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>), AppDomain.CurrentDomain.GetAssemblies());

    foreach (var assembly in assemblies)
        foreach (var type in assembly.GetExportedTypes().Where(a => !a.IsAbstract))
        {
            poormanslogger(string.Format("Exported Type: {0}", type.Name));
            foreach (var @interface in type.GetInterfaces())
            {
                poormanslogger(string.Format("\tInterface type: {0}", @interface.Name));
                // register open generic
                if (@interface.IsGenericType && !type.IsAbstract && type.IsGenericType)
                    container.RegisterManyForOpenGeneric(type, assemblies);
                // register closed generic
                if (@interface.IsGenericType && !type.IsAbstract && !type.IsGenericType)
                    container.Register(@interface, type, Lifestyle.Transient);
                // register non generic
                if (!type.IsGenericType && !@interface.IsGenericType && !type.IsAbstract)
                    container.Register(@interface, type, Lifestyle.Transient);
            }

            // unregistered type fall back...
            //container.RegisterOpenGeneric(typeof(ICommandHandler<>), typeof(UnRegisteredCommandHandler<>));
        }

    // The decorators are applied in the order in which they are registered
    container.RegisterDecorator(typeof(ICommandHandler<>),
            typeof(TransactionCommandHandlerDecorator<>));

    container.RegisterDecorator(typeof(ICommandHandler<>),
        typeof(DeadlockRetryCommandHandlerDecorator<>));

    container.RegisterDecorator(typeof(ICommandHandler<>),
        typeof(ValidationCommandHandlerDecorator<>));

    container.RegisterDecorator(typeof(ICommandHandler<>),
        typeof(SecurityCommandHandlerDecorator<>));
}
}

然后我有了标准的东西:

Then I have the standard stuff:

    var container = new Container();

    container.RegisterPackages();

    container.Verify();

    var instance = container.GetInstance<ICommandHandler<UnregisteredCommand>>();
    instance.Handle(new UnregisteredCommand());

是否可以使用ExpressionBuilding事件?

Could I possibly use the ExpressionBuilding event?

推荐答案

Simple Injector提供的批处理注册功能仅限于通用类型的注册。文档解释原因:

The batch-registration facilities that Simple Injector offers limits itself to registration of generic types. The documentation explains why:


尽管许多其他DI库都包含用于执行
基于约定的注册的高级API,但我们发现使用自定义
LINQ查询可以更轻松地编写,更容易理解,并且通常
比使用预定义和限制性API更加灵活。

Although many other DI libraries contain an advanced API for doing convention based registration, we found that doing this with custom LINQ queries is easier to write, more understandable, and can often prove to be more flexible than using a predefined and restrictive API.

通用接口类型的批处理注册,没有内置的支持来区分生活方式。这是非常故意的,因为这将迫使您在类上放置库定义的属性以描述生活方式,并且这将使您的应用程序依赖于Simple Injector,这就是我们要防止

But even for batch-registrations for types of a generic interface there's no built-in support to differentiate between lifestyles. This is quite deliberate, since this would force you to place library defined attributes on your classes to describe the lifestyle, and this would make your application dependent on Simple Injector, which is something we want to prevent.

Simple Injector 2.6引入了一个名为生活方式选择行为的新扩展点。当没有明确提供生活方式时,此扩展点使您可以覆盖框架的默认选择行为。默认行为是使用 Lifestyle.Transient

Simple Injector 2.6 introduces a new extension point called the lifestyle selection behavior. This extension point allows you to override the default selection behavior of the framework when no lifestyle is supplied explicitly. The default behavior is to use Lifestyle.Transient.

如果您需要类似的内容,则应覆盖默认值生活方式选择行为,并创建可应用于组件的属性。看起来可能像这样:

If you need something like this, you should override the default lifestyle selection behavior and create an attribute that you can apply to your components. That could look something like this:

public enum CreationPolicy { Transient, Scoped, Singleton };

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface,
    Inherited = false, AllowMultiple = false)]
public sealed class CreationPolicyAttribute : Attribute {
    public CreationPolicyAttribute(CreationPolicy policy) {
        this.Policy = policy;
    }

    public CreationPolicy Policy { get; private set; }
}

此属性应在应用程序的基础项目中定义,因此每个类可以用它标记:

This attribute should be defined in a base project of your application so each class can be marked with it:

[CreationPolicy(CreationPolicy.Scoped)]
public class CustomerRepository : IRepository<Customer>
{
    public CustomerRepository(DbContext context) { }

    public Customer GetById(Guid id) {
        // etc.
    }
}

使用此定义的属性,我们现在可以如下定义自定义生活方式选择行为:

Using this defined attribute, we can now define our custom lifestyle selection behavior as follows:

public class AttributeLifestyleSelectionBehavior : ILifestyleSelectionBehavior {
    private const CreationPolicy DefaultPolicy = CreationPolicy.Transient;
    private readonly ScopedLifestyle scopedLifestyle;

    public AttributeLifestyleSelectionBehavior (ScopedLifestyle scopedLifestyle) {
        this.scopedLifestyle = scopedLifestyle;
    }

    public Lifestyle SelectLifestyle(Type serviceType, Type implementationType) {
       var attribute = implementationType.GetCustomAttribute<CreationPolicyAttribute>()
           ?? serviceType.GetCustomAttribute<CreationPolicyAttribute>();

       var policy = attribute == null ? DefaultPolicy : attribute.Policy;

       switch (policy) {
           case CreationPolicy.Singleton: return Lifestyle.Singleton;
           case CreationPolicy.Scoped: return this.scopedLifestyle;
           default: return Lifestyle.Transient;
       }
    }
}

可以注册此自定义行为

var container = new Container();

// This is the scoped lifestyle for our application type.
ScopedLifestyle scopedLifestyle = new WebRequestLifestyle();

container.Options.LifestyleSelectionBehavior =
    new AttributeLifestyleSelectionBehavior(scopedLifestyle);

通过此操作,我们简化了批处理注册,并且无需执行任何特殊操作即可选择合适的生活方式:

With this in place we simplified the batch registration, and we don't have to do anything special to select the proper lifestyle:

container.RegisterOpenGeneric(
    typeof(ICommandHandler<>),
    AppDomain.CurrentDomain.GetAssemblies());

var registrations =
    from assembly in assemblies
    from type in assembly.GetExportedTypes()
    where !type.IsAbstract && !type.IsGenericType
    from @interface in type.GetInterfaces()
    where !@interface.IsGenericType
    select new { Service = @interface, Implementation = type };

foreach (var registration in registrations)
{
    container.Register(registration.Service, registration.Implementation);
}

除了覆盖生活方式选择行为外,另一种选择是将所有内容都注册为瞬态(因为这将是一个明智的默认值),但会覆盖应通过其他方式注册的少数注册:

Instead of overriding the lifestyle selection behavior, another option is to have everything register as transient (since that would be a sensible default), but override those few registrations that should be registered otherwise:

container.RegisterOpenGeneric(
    typeof(ICommandHandler<>),
    AppDomain.CurrentDomain.GetAssemblies());

container.Options.EnableOverridingRegistrations = true;

container.Register<ICommandHandler<SpecialOperation>, SpecialOperationHandler>(
    scopedLifestyle);

container.Options.EnableOverridingRegistrations = false;

这篇关于如何使用自动注册对单个图书馆进行范围划分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆