如何使用自动注册来确定单个库的范围

本文关键字:单个库 范围 何使用 注册 | 更新日期: 2023-09-27 18:17:12

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

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<>));
}
}

然后我有标准的东西:

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

我可以使用ExpressionBuilding事件吗?

如何使用自动注册来确定单个库的范围

Simple Injector提供的批处理注册功能限制了它自己对泛型类型的注册。文档解释了原因:

尽管许多其他的DI库包含一个高级的API来做基于约定的注册,我们发现用自定义来做这个LINQ查询更容易编写,更容易理解,并且通常可以被证明比使用预定义的限制性API更灵活。

但是,即使对于通用接口类型的批量注册,也没有内置支持来区分不同的生活方式。这是经过深思熟虑的,因为这将迫使你在类上放置库定义的属性来描述生活方式,这将使你的应用依赖于简单注入器,这是我们想要防止的。

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

如果您需要这样的东西,您应该覆盖默认的生活方式选择行为,并创建一个可以应用于组件的属性。可以像这样:

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; }
}

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

[CreationPolicy(CreationPolicy.Scoped)]
public class CustomerRepository : IRepository<Customer>
{
    public CustomerRepository(DbContext context) { }
    public Customer GetById(Guid id) {
        // etc.
    }
}
使用这个已定义的属性,我们现在可以定义自定义的生活方式选择行为,如下所示:
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);

有了这个,我们简化了批注册,我们不需要做任何特别的事情来选择合适的生活方式:

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);
}

不覆盖生活方式选择行为,另一种选择是将所有内容注册为暂态(因为这将是一个明智的默认值),但覆盖那些应该以其他方式注册的少数注册:

container.RegisterOpenGeneric(
    typeof(ICommandHandler<>),
    AppDomain.CurrentDomain.GetAssemblies());
container.Options.EnableOverridingRegistrations = true;
container.Register<ICommandHandler<SpecialOperation>, SpecialOperationHandler>(
    scopedLifestyle);
container.Options.EnableOverridingRegistrations = false;