0

My application deals with multiple clients and each client has different settings. So I have decided to use IOC container to register each client specific settings using a key. Then i am locating ClientSettings based on akey. key value will be available during runtime.

C# Library MyCompany.BO

public class ClientSettings : IClientSettings
{

    public string ClientApiKey { get; private set}
    public string ClientId { get; private set}

    public static IClientSettings Load(string clientname)
    {
        // Load the client specific information from JSON file using clientname
        var cs = new ClientSettings();
        cs.ClientApiKey = "Some client specific key";
        cs.ClientId = "Some client specific key";
        return cs;
    }
}

Consumed in MyCompany.BO namespace

public class RuleEngine : IRuleEngine
{
    IFactory _factory;
    RuleEngine(IFactory factory)
    {
        factory = _factory;
    }

    public void Run(string request)
    {
        var clientname = ParseStringToGetClientName(request)


           var clientSettings = ServiceLocator.Current.GetInstance<IClientSettings>(clientname);

        var service = _factory.GetService(clientname);

        service.DoWork(clientSettings);
    }
}

Main Application registering the client settings

var container = new UnityContainer();

var clientASettings = ClientSettings.Load("ClientA");
var clientBASettings = ClientSettings.Load("ClientB");
var clientCSettings = ClientSettings.Load("ClientC");

// register singleton instance
container.RegisterInstance("ClientA", clientASettings);
container.RegisterInstance("ClientB", clientBSettings);
container.RegisterInstance("ClientC", clientCSettings);

Question
The RuleEngine class is using Microsoft.Practices.ServiceLocation.ServiceLocator to locate ClientSettings based on the key. That means C# library where rule engine is implemented needs to use the same IOC that application is using. (Which is Unity in this case)
How do I make library unaware of IOC container?
How do I inject clientsettings here without using ServiceLocator when the key value known during runtime? Is the factory pattern the only way to achieve this? (For example the way I am locating service _factory.GetService(clientname))

Eric Herlitz
  • 25,354
  • 27
  • 113
  • 157
LP13
  • 30,567
  • 53
  • 217
  • 400
  • 2
    Take a look at the concept of the [Composition Root](http://blog.ploeh.dk/2011/07/28/CompositionRoot/) and in case you are writing a reusable library, read [this article](http://blog.ploeh.dk/2014/05/19/di-friendly-library/). – Steven Aug 20 '16 at 09:21
  • @Steven Thanks. I looked at Composition Root, in short its the location in your application where you would register types with container. That's the only place we should use container. and that's exactly I'm doing. My application is MVC application so all types will register in global.ascx with container as suggested. However this doesn't not the solve issue I posted...(I have also gone through the other link) – LP13 Aug 21 '16 at 06:43
  • To answer part of your question: no, factories are not the only way, nor the best way to achieve that. See this: https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=100 – Steven Aug 21 '16 at 06:50
  • @Steven Thanks. However based on the post http://stackoverflow.com/questions/2045904/dependency-inject-di-friendly-library/2047657#2047657 by Mark Seemann `Use Abstract Factory if you need a short-lived object or to construct the dependency based on a value known only at run-time.` – LP13 Aug 21 '16 at 07:11
  • and also here http://stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-via-a-di-container/1945023#1945023 – LP13 Aug 21 '16 at 07:42
  • Mark was wrong about that. I think my blog post makes a compelling argument. – Steven Aug 21 '16 at 07:50
  • 1
    I think it's fair to sum up my current thinking about this topic as: 1. Abstract Factory is better than a Service Locator. 2. Alternative selection mechanisms are often better than Abstract Factory. See e.g. [Partial Type Name Role Hint](http://blog.ploeh.dk/2013/01/11/PartialTypeNameRoleHint), [Role Interface Role Hint](http://blog.ploeh.dk/2013/01/10/RoleInterfaceRoleHint), and [Metadata Role Hint](http://blog.ploeh.dk/2013/01/09/MetadataRoleHint) for some alternatives. – Mark Seemann Aug 21 '16 at 11:23
  • Initially I though of using classic Singleton pattern for clientsettings class. like http://stackoverflow.com/questions/12730210/best-pattern-for-storing-common-settings-across-multiple-classes but then I guess UnitTesting would be an issue, http://stackoverflow.com/questions/2085953/unit-testing-with-singletons. So I have decided to use IOC and register the instance as singleton. – LP13 Aug 21 '16 at 20:30

1 Answers1

4

These three lines:

var clientname = ParseStringToGetClientName(request);    
var clientSettings = ServiceLocator.Current.GetInstance<IClientSettings>(clientname);        
var service = _factory.GetService(clientname);

look like pure plumbing with little or no branching. Move that part to your Composition Root. Since you've put this in your Composition Root, there's no reason to attempt to abstract away any DI Container you may use. Instead, you can simply compose the desired services using the API of the DI Container, and the reusable class library will not need to know how that happened.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • The services are also resolved based on the runtime value. If i decided to register services with DI container at composition root as you suggest, how would you get instance of service without using factory or service locator? ( this is the same issue with client settings). The only difference between services and clientsettings is, For each requeset I want new instance of service, while Clientsettings could be singleton. – LP13 Aug 21 '16 at 20:25
  • also whichever the approach I used ( factory or other sophisticated options) it needs to return singleton instance that is register with the container in Composition Root – LP13 Aug 21 '16 at 20:35
  • 2
    [A DI container encapsulated inside the composition root is not a Service Locator](http://blog.ploeh.dk/2011/08/25/ServiceLocatorrolesvs.mechanics/). This means that your factory implementation can call the container as long as it is located *inside* the composition root. – Steven Aug 22 '16 at 06:26