覆盖StructureMap 3中的自定义注册约定



我在最近的项目中使用CQRS模式,并使用Structuremap 3作为我的IoC Container,因此我定义了以下转换来解决每个BaseEntity类型的ICommandHandlers:

 public class InsertCommandRegistrationConvention 
    : StructureMap.Graph.IRegistrationConvention
{
    private static readonly Type _openHandlerInterfaceType = typeof(ICommandHandler<>);
    private static readonly Type _openInsertCommandType = typeof(InsertCommandParameter<>);
    private static readonly Type _openInsertCommandHandlerType = typeof(InsertCommandHandler<>);
    public void Process(Type type, Registry registry)
    {
        if (!type.IsAbstract && typeof(BaseEntity).IsAssignableFrom(type) &&
            type.GetInterfaces().Any(x => x.IsGenericType && 
                x.GetGenericTypeDefinition() == typeof(IAggregateRoot<>)))
        {
            Type closedInsertCommandType = _openInsertCommandType.MakeGenericType(type);
            Type closedInsertCommandHandlerType =
                _openInsertCommandHandlerType.MakeGenericType(type);
            Type insertclosedHandlerInterfaceType =
                _openHandlerInterfaceType.MakeGenericType(closedInsertCommandType);
            registry.For(insertclosedHandlerInterfaceType)
                .Use(closedInsertCommandHandlerType);
        }
    }
}

并在我的CompositionRoot中使用:

public static class ApplicationConfiguration
{
    public static IContainer Initialize()
    {
        ObjectFactory.Initialize(x =>
        {
            x.Scan(s =>
            {
                s.TheCallingAssembly();
                s.WithDefaultConventions();
                s.Convention<InsertCommandRegistrationConvention>();
            });
        });
        return ObjectFactory.Container;
    }
}

那么对于我的每个实体它都会注册相应的InsertCommandHandler例如它会注册

InsertCommandHandler<InsertCommandParameter<Order>> for ICommandHandler<ICommandParameter<Order>>

有时我需要为某些实体注册自定义InsertCommandHandler s,例如Product,我想为ICommandHandler<ICommandParameter<Product>>而不是InsertCommandHandler<InsertCommandParameter<Product>>注册非通用InsertProductCustomCommandHandler类(换句话说,我想覆盖InsertCommendRegistrationConvention)。

我怎么能做到这一点,与Structuremap 3?

您可以使用IContainer.Configure()方法来完成此操作- Configure()方法允许您向现有的ContainerObjectFactory添加额外的配置

我已经简化了您的抽象,以便在实际操作中演示:

public abstract class BaseEntity { }
public interface ICommandHandler<T> { }
public class ClassA : BaseEntity { }
public class ClassB : BaseEntity { }
public class ClassC : BaseEntity { }
public class ClassD : BaseEntity { }
public class InsertCommandHandler<T> : ICommandHandler<T> { }
public class SpecialInsertDCommandHandler : ICommandHandler<ClassD> { }

InsertCommandRegistrationConvention

public class InsertCommandRegistrationConvention : IRegistrationConvention
{
    private static readonly Type _openHandlerInterfaceType = 
        typeof(ICommandHandler<>);
    private static readonly Type _openInsertCommandHandlerType = 
        typeof(InsertCommandHandler<>);
    public void Process(Type type, Registry registry)
    {
        if (!type.IsAbstract && typeof(BaseEntity).IsAssignableFrom(type))
        {
            Type closedInsertCommandHandlerType =
                _openInsertCommandHandlerType.MakeGenericType(type);
            Type insertclosedHandlerInterfaceType =
                _openHandlerInterfaceType.MakeGenericType(type);
            registry.For(insertclosedHandlerInterfaceType)
                .Use(closedInsertCommandHandlerType);
        }
    }
}

这个测试演示了用InsertCommandRegistrationConvention配置的容器将返回所有4个测试类ClassAClassD的通用InsertCommandHandler<>

[Test]
public void Handle_Initialize_RegistersClassesAToDToReturnInsertCommandHandler()
{
    var container = ApplicationConfiguration.Initialize();
    var resultA = container.GetInstance<ICommandHandler<ClassA>>();
    var resultB = container.GetInstance<ICommandHandler<ClassB>>();
    var resultC = container.GetInstance<ICommandHandler<ClassC>>();
    var resultD = container.GetInstance<ICommandHandler<ClassD>>();
    Assert.That(resultA.GetType() == typeof(InsertCommandHandler<ClassA>));
    Assert.That(resultB.GetType() == typeof(InsertCommandHandler<ClassB>));
    Assert.That(resultC.GetType() == typeof(InsertCommandHandler<ClassC>));
    Assert.That(resultD.GetType() == typeof(InsertCommandHandler<ClassD>));
}

这个测试显示Configure方法成功地更新了ClassD的注册,返回SpecialInsertDCommandHandler而不是InsertCommandHandler<ClassD>

[Test]
public void Handle_Condfigure_OverridesRegistrationForClassD()
{
    var container = ApplicationConfiguration.Initialize();
    container.Configure(x =>
    {
        x.For<ICommandHandler<ClassD>>().Use<SpecialInsertDCommandHandler>();
    });
    var resultD = container.GetInstance<ICommandHandler<ClassD>>();
    Assert.That(resultD.GetType() == typeof(SpecialInsertDCommandHandler));
}

您可以将约定修改为"如果有特定的命令处理程序,则使用该处理程序"。否则,使用InsertCommandHandler。"

它看起来像这样(像quick一样,我简化了类):

public class InsertCommandRegistrationConvention : IRegistrationConvention
{
    private static readonly Type _openHandlerInterfaceType = typeof (ICommandHandler<>);
    private static readonly Type _openInsertCommandHandlerType = typeof (InsertCommandHandler<>);
    private static readonly IList<Type> _customCommandHandlerTypes;
    static InsertCommandRegistrationConvention()
    {
        _customCommandHandlerTypes = _openInsertCommandHandlerType
            .Assembly
            .ExportedTypes
            .Where(x => !x.IsAbstract)
            .Where(x => !x.IsGenericType || x.GetGenericTypeDefinition() != typeof (InsertCommandHandler<>))
            .Where(x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommandHandler<>)))
            .ToArray();
    }
    public void Process(Type type, Registry registry)
    {
        if (!type.IsAbstract && typeof (BaseEntity).IsAssignableFrom(type))
        {
            var insertclosedHandlerInterfaceType = _openHandlerInterfaceType.MakeGenericType(type);
            var closedInsertCommandHandlerType = _openInsertCommandHandlerType.MakeGenericType(type);
            // check for any classes that implement ICommandHandler<T> that are not also InsertCommandHandler<T>
            var customHandler = _customCommandHandlerTypes.FirstOrDefault(t => t.GetInterfaces().Any(i => i == insertclosedHandlerInterfaceType));
            registry.For(insertclosedHandlerInterfaceType)
                .Use(customHandler ?? closedInsertCommandHandlerType);
        }
    }
}

我使用的类:

public abstract class BaseEntity { }
public class Class1 : BaseEntity { }
public class Class2 : BaseEntity { }
public class SpecialClass :BaseEntity { }
public interface ICommandHandler<T> { }
public class InsertCommandHandler<T> : ICommandHandler<T> { }
public class SpecialClassInsertCommandHandler : ICommandHandler<SpecialClass> { }

通过测试:

        Assert.That(ObjectFactory.GetInstance<ICommandHandler<Class1>>(), Is.InstanceOf<InsertCommandHandler<Class1>>());
        Assert.That(ObjectFactory.GetInstance<ICommandHandler<Class2>>(), Is.InstanceOf<InsertCommandHandler<Class2>>());
        Assert.That(ObjectFactory.GetInstance<ICommandHandler<SpecialClass>>(), Is.InstanceOf<SpecialClassInsertCommandHandler>());

这样做的好处是修改了约定,而不是避免对某些项使用约定。如果你的逻辑需要改变,它可以将所有内容保持在一个位置。

最新更新