IoC, Unity and passing parameters (or a way to avoid doing so)

inversion-of-control

While the concept of IoC isn't foreign to me, I'm new to Unity and I'm having trouble connecting the metaphorical dots, so to speak.

In our project we have a class library for logic, then several class libraries with repositories implementing common interfaces (they provide access and allow CRUD operations on different data sources). Finally, this is all used together in either an MVC web site or a SharePoint site.

We wanted to implement Unity to keep a single configuration and make the whole thing better.

But… there are some design issues I'm facing. For example – the MVC web site, as far as communicating with the underlying SQL database is concerned, can have everything it needs specified in the configuration section for the Unity container. However, said web site also needs to occasionally connect with SharePoint… in order to create a SharePoint repository, I need to create an SPWeb object and pass it to the repository constructor.

I CAN do that using ParameterOverride when resolving the type, but this doesn't… feel right. There are cases where a larger number of parameters need to overridden this way; to resolve a logic class I need to pass repositories, which in turn are resolved by passing a manually created SPWeb object… When this happens it seems I'm no longer following an IoC pattern. The kicker here is that I cannot have the SPWeb object defined in the Unity container!

Is there a pattern (a practical example would be superb) where Unity is used but certain values are being passed TO the container while resolving types, without the need to use ParameterOverride?


EDIT

I feel I need to provide a more specific example of why ParameterOverride is a problem. Below is a fragment of code used by a SharePoint timer job. The job in question synchronizes some data between SharePoint and SQL, so it requires two different repositories implementing the same interface. Because the code is ran from SharePoint, it cannot access a global configuration defined DataContext (EntityFramework, code-first) because there's no web.config file to read the connection string from. Also, due to the weirdness that is SharePoint, I cannot "get" the current SPWeb object, and instead need to create it on the fly.

The end result is a ton on using statements, and a lot of parameters need to overridden when using Resolve. Also, the need to pass the parameter NAME as a string would make this really difficult to code for anyone else – this information isn't known from the interface alone.

    public override void Execute(Guid targetInstanceId)
    {
        var siteUrl = Properties["Site"] as string;
        var sqlConnection = Properties["SQL"] as string;
        if (String.IsNullOrEmpty(siteUrl))
            throw new ArgumentNullException("Site URL is missing.");
        if (String.IsNullOrEmpty(sqlConnection))
            throw new ArgumentNullException("SQL connection string is missing.");

        using (var site = new SPSite(siteUrl, SPUserToken.SystemAccount))
        using (var web = site.OpenWeb())
        using (var spPollRepository = Configuration.ServiceLocator.SharePointContainer.Container.Resolve<IPollRepository>(new ParameterOverride("web", web)))
        using (var ctx = Configuration.ServiceLocator.SharePointContainer.Container.Resolve<Repositories.PollsDataContext>(Configuration.ServiceLocator.SqlNamedRegistration, new ParameterOverride("nameOrConnectionString", sqlConnection), new ParameterOverride("initialize", false)))
        using (var sqlPollRepository = Configuration.ServiceLocator.SharePointContainer.Container.Resolve<IPollRepository>(Configuration.ServiceLocator.SqlNamedRegistration, new ParameterOverride("ctx", ctx)))
        {
            IPollsSyncLogic logic = Configuration.ServiceLocator.SharePointContainer.Container.Resolve<Logic.IPollsSyncLogic>(new ParameterOverride("sqlRepository", sqlPollRepository), new ParameterOverride("spRepository", spPollRepository));
            logic.SyncPolls();
        }
    }

There's a big chance I'm using Unity… wrong. Or that some other fundamental definition has been mixed up. I hope this clears up any confusion regarding the question.

Best Answer

If the entities returned by the SharePoint repository are specific, just create a [TheEntity]Repository interface with a single implementation that talks to SharePoint.

If the [TheEntity]Repository interface has two implementations that talk respectively to SharePoint and a SQL database, you could use ResolvedParameters at configuration time (not Overrides at resolve time) to inject the right concrete repository depending on the type that is being injected into.

Edit

Seeing the code you posted, the big flaw is that you would like to use IOC and abstract repositories, but this is ruined by the fact that IPollsSyncLogic perfectly knows about Sql and Sharepoint, since its constructor has two parameters explicitly named sqlRepository and spRepository.

You should either

  • Fully embrace the specificity of IPollsSyncLogic as an SP to SQL synchronizer by using explicit SQL and SP repository types in its constructor signature.

or

  • Make IPollsSyncLogic a more abstract synchronizer between any two implementations of the same repository interface. In that case, just name its constructor parameters something like repositorySource and repositoryTarget. In your IoC configuration, use an InjectionFactory to specify the two concrete repository types.

As a side note, you should avoid resorting to Container.Resolve and rely on automatic resolution based on configuration when possible, because discrete calls to the container are usually hidden dependencies. For dependencies that are part of using statements and can't be injected through a constructor, you can rely on a Factory that produces them on the fly.

Regarding your problem with SharePoint accessing DataContext, I think it deserves a separate question.