2

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.

  • Under which conditions is it SharePoint instead of SQL ? As part of the normal application execution flow ? When you change something in the config file ? How far from composition point (Controller) is that SQL vs SharePoint decision made ? – guillaume31 Nov 19 '14 at 13:36
  • Certain views (and subsequently controllers for these views) need to display SharePoint data on the fly. But that was just one example - the same issue occurs from SharePoint side, which occasionally needs to connect to the SQL server. Heck, in certain cases (ex. SharePoint events or timer jobs) also need to provide SPWeb because in these cases SPContext.Current is unavailable... – Shaamaan Nov 19 '14 at 14:49
  • 1
    Nested using statements can be grouped together, which may help with readability – Jack Nov 20 '14 at 10:23
  • 1
    @Jack Thanks! That helps readability! It doesn't solve the issue, but damn, that's good to know! :) I've added that change, and also updated to question to make it also more readable. – Shaamaan Nov 20 '14 at 10:35
1

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.

  • I... don't quite understand what you mean by [TheEntity]Repository. I code in C#, so this is rather confusing. Secondly, there's a common interface for performing CRUD operations on any given model object, and two implementations - one for SQL and another for SharePoint (in theory there could be more, but lets stick to this for now). Also, ResolvedParameters won't solve the problem. As I wrote in a comment - SharePoint can be weird and sometimes in SharePoint I can use SPContext.Current to get the current SPWeb, and in other places I cannot. – Shaamaan Nov 20 '14 at 7:44
  • By entity I mean whatever object returned by the repository. Could be UserRepository, ArticleRepository, anything. But we may not have the same definition of repository. Why won't ResolvedParameters solve the problem ? What does this have to do with SPContext ? I guess without some code to explain it, the question is a bit blurry. – guillaume31 Nov 20 '14 at 8:11
  • Agreed. I added some sample code to my question. – Shaamaan Nov 20 '14 at 10:02
  • It's much clearer with code :) See my edit. – guillaume31 Nov 20 '14 at 12:42
  • OK, fair point about the parameter names of the Logic class, but it IS generic. ;) I used the specific names because it was easier, during writing, to remember which is which. So... An InjectorFactory then. I... uhm... haven't used that yet. Well, at least I know what to look for! :) – Shaamaan Nov 20 '14 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.