Question

I have recently been using dependency injection (Unity) to achieve low coupling between my domain layers and any infrastructural concerns.

I have ended up with the a lot of Unity container code in my MVC bootstrapper.

A small snippet:

container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>("FirstContext", new PerResolveLifetimeManager(), new InjectionConstructor(new FirstContext()));
container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>("AnotherContext", new PerResolveLifetimeManager(), new InjectionConstructor(new AnotherContext()));

// User Aggregate
container.RegisterType<IEntityMapper<User, UserTable>, UserMapper>();
container.RegisterType<IUserRepository, UserRepository>(
    new InjectionConstructor(
        new ResolvedParameter<IUnitOfWork>("FirstContext"),
        new ResolvedParameter<IEntityMapper<User, UserTable>>()
    )
);

// Token Aggregate
container.RegisterType<IEntityMapper<Token, TokenTable>, TokenMapper>();
container.RegisterType<ITokenRepository, TokenRepository>(
    new InjectionConstructor(
        new ResolvedParameter<IUnitOfWork>("FirstContext"),
        new ResolvedParameter<IEntityMapper<Token, TokenTable>>()
    )
);

// Payment Aggregate
container.RegisterType<IReadOnlyEntityMapper<Payment, PaymentTable>, PaymentFactory>();
container.RegisterType<IPaymentRepository, PaymentRepository>(
    new InjectionConstructor(
        new ResolvedParameter<IUnitOfWork>("FirstContext"),
        new ResolvedParameter<IReadOnlyEntityMapper<Payment, PaymentTable>>()
    )
);

// Customer Aggregate
container.RegisterType<IReadOnlyEntityMapper<Customer, CustomerTable>, CustomerMapper>();
container.RegisterType<ICustomerRepository, CustomerRepository>(
    new InjectionConstructor(
        new ResolvedParameter<IUnitOfWork>("AnotherContext"),
        new ResolvedParameter<IReadOnlyEntityMapper<Customer, CustomerTable>>()
    )
);

// Country Aggregate
container.RegisterType<IReadOnlyEntityMapper<Country, CountryTable>, CountryMapper>();
container.RegisterType<ICountryRepository, CountryRepository>(
    new InjectionConstructor(
        new ResolvedParameter<IUnitOfWork>("AnotherContext"),
        new ResolvedParameter<IReadOnlyEntityMapper<Country, CountryTable>>()
    )
);

// Province Aggregate
container.RegisterType<IReadOnlyEntityMapper<Province, ProvinceTable>, ProvinceMapper>();
container.RegisterType<IProvinceRepository, ProvinceRepository>(
    new InjectionConstructor(
        new ResolvedParameter<IUnitOfWork>("AnotherContext"),
        new ResolvedParameter<IReadOnlyEntityMapper<Province, ProvinceTable>>()
    )
);

Is there a better way to organise this?

I can't seem to find any examples/articles/direction online.

Was it helpful?

Solution

Typically what you would do is create "Modules" for each logical component of your application and set the mappings in the Initialize method of the module. In your bootstrapper, you set which modules to you want to load. There are multiple ways to do this. You can explicitly do it in code, in XML, or by using DLL Discovery.

Normally you won't set the mappings in the bootstrapper. You just decide what you want to load in the bootstrapper and allow the module to set the mappings it needs.

If your question is how do you prevent having to do the mappings at all (i.e. no container.RegisterInstance<...>) anywhere in code, it's not possible as far as I know. The information on how to map the Interfaces have to come from SOMEWHERE.

http://msdn.microsoft.com/en-us/library/gg405479(v=pandp.40).aspx

EDIT:

Create a Module for each logical section. I'll do one for FirstContext and AnotherContext. I usually put a module per DLL, but there's nothing stopping you from creating more than 1 module per DLL. It's up to you how you want to organize it.

FirstContextModule:

public class FirstContextModule : IModule
{
    private readonly IUnityContainer container;

    public FirstContextModule(IUnityContainer unityContainer)
    {
        this.container = unityContainer;
    }

    public void Initialize()
    {
        this.SetupContainer();
        this.SetupRegions();
    }

    private void SetupContainer()
    {
        container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>("FirstContext", new PerResolveLifetimeManager(), new InjectionConstructor(new FirstContext()));

        // User Aggregate
        container.RegisterType<IEntityMapper<User, UserTable>, UserMapper>();
        container.RegisterType<IUserRepository, UserRepository>(
            new InjectionConstructor(
                new ResolvedParameter<IUnitOfWork>("FirstContext"),
                new ResolvedParameter<IEntityMapper<User, UserTable>>()));

        // Token Aggregate
        container.RegisterType<IEntityMapper<Token, TokenTable>, TokenMapper>();
        container.RegisterType<ITokenRepository, TokenRepository>(
            new InjectionConstructor(
                new ResolvedParameter<IUnitOfWork>("FirstContext"),
                new ResolvedParameter<IEntityMapper<Token, TokenTable>>()));

        // Payment Aggregate
        container.RegisterType<IReadOnlyEntityMapper<Payment, PaymentTable>, PaymentFactory>();
        container.RegisterType<IPaymentRepository, PaymentRepository>(
            new InjectionConstructor(
                new ResolvedParameter<IUnitOfWork>("FirstContext"),
                new ResolvedParameter<IReadOnlyEntityMapper<Payment, PaymentTable>>()));
    }

    private void SetupRegions()
    {
        // Register the views
    }
}

AnotherContextModule:

public class AnotherContextModule : IModule
{
    private readonly IUnityContainer container;

    public AnotherContextModule(IUnityContainer unityContainer)
    {
        this.container = unityContainer;
    }

    public void Initialize()
    {
        this.SetupContainer();
        this.SetupRegions();
    }

    private void SetupContainer()
    {
        container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>("AnotherContext", new PerResolveLifetimeManager(), new InjectionConstructor(new AnotherContext()));

        // Customer Aggregate
        container.RegisterType<IReadOnlyEntityMapper<Customer, CustomerTable>, CustomerMapper>();
        container.RegisterType<ICustomerRepository, CustomerRepository>(
            new InjectionConstructor(
                new ResolvedParameter<IUnitOfWork>("AnotherContext"),
                new ResolvedParameter<IReadOnlyEntityMapper<Customer, CustomerTable>>()));

        // Country Aggregate
        container.RegisterType<IReadOnlyEntityMapper<Country, CountryTable>, CountryMapper>();
        container.RegisterType<ICountryRepository, CountryRepository>(
            new InjectionConstructor(
                new ResolvedParameter<IUnitOfWork>("AnotherContext"),
                new ResolvedParameter<IReadOnlyEntityMapper<Country, CountryTable>>()));

        // Province Aggregate
        container.RegisterType<IReadOnlyEntityMapper<Province, ProvinceTable>, ProvinceMapper>();
        container.RegisterType<IProvinceRepository, ProvinceRepository>(
            new InjectionConstructor(
                new ResolvedParameter<IUnitOfWork>("AnotherContext"),
                new ResolvedParameter<IReadOnlyEntityMapper<Province, ProvinceTable>>()));
    }

    private void SetupRegions()
    {
        // Register the views
    }
}

Bootstrapper:

public class Bootstrapper : UnityBootstrapper
{
    protected override void ConfigureModuleCatalog()
    {
        base.ConfigureModuleCatalog();

        ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
        moduleCatalog.AddModule(typeof(FirstContextModule ));
        moduleCatalog.AddModule(typeof(AnotherContextModule ));
    }

    protected override DependencyObject CreateShell()
    {
        return this.Container.Resolve<Shell>();
    }

    protected override void InitializeShell()
    {
        base.InitializeShell();

        Application.Current.MainWindow = (Window)this.Shell;
        Application.Current.MainWindow.Show();
    }
}

Now that it's separated into modules, let's say you want 3 different versions of your application:
1 that only uses FirstContext 1 that only uses AnotherContext 1 that uses both FirstContext and AnotherContext

All you would need to do is change ConfigureModuleCatalog() to only add the module you want to use.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top