简体   繁体   中英

Simple Injector and assembly scanning

I'm finally diving into the process of learning about IoC and other patterns and have run into an issue when trying to register open generics based on a list of assemblies. I'm building the core of my framework into separate class libraries and have a set of test projects where I declare some test classes inheriting from my framework.

Problem is that the framework.test.service dll where AddPersonCommandHandler : ICommandHandler<AddPersonCommand> isn't loaded at runtime so the ICommandHandler<> open generic fails to find anything. I need to create an AddPersonCommandHandler instance in advance then I get one extra assembly in my list and then it works.

Is there a simple way to force loading?

=========== EDIT: So this is the wireup code in my unit test project which holds the app domain and references to the other assemblies.

public Container SetupIoc()
{
    var container = new Container();

    container.Register(typeof(IUnitOfWork), typeof(EntityFrameworkUnitOfWork), Lifestyle.Singleton);

    var assemblies = AppDomain.CurrentDomain.GetAssemblies();

    container.Register(typeof(ICommandHandler<>), assemblies);


    container.Verify();

    return container;
}

IUnitOfWork is in Database.Interface.dll ICommandHandler<> is in Core.dll AddPersonCommandHandler is in Test.Service.dll -- Part of a suite of projects settings up structure mimicking putting a real solution together and referenced by the Unit Tests.

So, what's happening is that the unit or work registration works fine I think because I'm specifying UnitOfWork directly whereas the ICommandHandler interface is in the Core which successfully binds. Post Verify() I only see the unit of work registration on container . To get the Test.Service.dll loading so the AddPersonCommandHandler show up I can instantiate an AddPersonCommand in my TestInit method.

It just seems a little mad to have to manually load all the dlls for the project. And for the matter if I scan the executing folder for dlls, some are already loaded...will loading again play nicely or does it require careful checks to find out if it is already loaded?

You don't have to explicitly create an instance of AddPersonCommandHandler to make sure the assembly is loaded. All you have to do is reference a type in the assembly (such as your AddPersonCommandHandler ) statically in code that runs before you call AppDomain.CurrentDomain.GetAssemblies() . For instance:

// Load BL assembly
Type type = typeof(AddPersonCommandHandler);

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

Best is to keep this reference in your Composition Root. This ensures that no matter from where the container is initialized (App_start or integration tests) the same assemblies are loaded.

A more explicit way is to make an explicit list of assemblies that should be used for registration:

// Adding the (redundant) namespace of the type makes it very clear to the
// user if this type is still located in the business layer.
Assembly[] businessLayerAssemblies = new[] {
    typeof(MyComp.MyApp.BusinessLayer.AddPersonCommandHandler).Assembly
}

container.Register(typeof(ICommandHandler<>), businessLayerAssemblies);
container.Register(typeof(IQueryHandler<,>), businessLayerAssemblies);
container.Register(typeof(IEventHandler<>), businessLayerAssemblies);
container.Register(typeof(IValidator<>), businessLayerAssemblies);

I prefer the latter approach, because this prevents scanning a bulk of assemblies that never contain useful types, while being very explicit about the assemblies (instead of implicitly referencing the type that happens to load its assembly).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM