Injecting a concrete implementation of a WCF ServiceContract based off value in a config file dymamically

287 views Asked by At

Say I have the following service contract and two concrete implementations:

[OperationContract]
public interface ISearchService {
    public ICollection<string> Search(string text);
}

[SearchServiceBehaviour]
public class SolrSearchService : ISearchService {
    public ICollection<string> Search(string text) {
        // Implementation...
    }
}

[SearchServiceBehaviour]
public class SqlSearchService : ISearchService {
    public ICollection<string> Search(string text) {
        // Implementation...
    }
}

These are attributed with a ServiceBehavior so that I can create the instance whilst the service is running based off a config file:

public class SearchServiceBehaviour : Attribute, IServiceBehavior {
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
        foreach (var item in serviceHostBase.ChannelDispatchers) {
            var dispatcher = item as ChannelDispatcher;

            if (dispatcher != null) {
                dispatcher.Endpoints.ToList().ForEach(e => {
                    e.DispatchRuntime.InstanceProvider = new SearchServiceInstanceProvider();
                });
            }
        }
    }
}

public class SearchServiceInstanceProvider : IInstanceProvider {
    public object GetInstance(InstanceContext instanceContext, Message message) {
        // Should cache and only do this at an interval.
        ConfigurationManager.RefreshSection("appSettings");
        var index = ConfigurationManager.AppSettings["UseSolr"] as string;

        bool UseSolr;
        bool.TryParse(index, out UseSolr);

        if (UseSolr)
            return new IndexedSearchService();
        else
            return new SearchService();
    }
}

My question is how can I inject the concrete implementation using Ninject based off a changing value in a config file? It seems to be I should be doing the following:

public class SearchServiceModule : NinjectModule {
    private bool UseSolr;

    public SearchServiceModule() {
        // Should cache and only do this at an interval.
        ConfigurationManager.RefreshSection("appSettings");
        var index = ConfigurationManager.AppSettings["UseSolr"] as string;

        bool.TryParse(index, out UseSolr);
    }

    public override Load() {
        if (UseSolr)
            Bind<ISearchService>().To<SolrSearchService>();
        else 
            Bind<ISearchService>().To<SqlSearchService>();
    }
}

And then in the InstanceProvider:

public object GetInstance(InstanceContext instanceContext, Message message) {
    return _kernel.Get<ISearchService>();
}

However, the bindings in Ninject don't change after I change the value in the config file. Is there a way to change the binding based off the value in the config file? Am I doing something wrong here?

2

There are 2 answers

0
Remo Gloor On BEST ANSWER

Your module does not behave as you expect because there is a field and a local variable with the same name.

Also have a look at conditional bindings. .When()

2
Drew Marsh On

You're self-hosting in a Windows service. Saving a .config file does not automatically cause the Windows Service to recycle and notice your .config change (unlike IIS). So if you're building your kernel at startup, there is no trigger to cause it to be rebuilt.

You will have to change your implementation to use the .When() method to conditionally provide the implementation based off of the config setting. That would look something like this:

public override Load() 
{ 
    Bind<ISearchService>().To<SolrSearchService>().When(r => ShouldUseSolr());
    Bind<ISearchService>().To<SqlSearchService>().When(r => !ShouldUseSolr());
}

private static ShouldUseSolr()
{
    // Should cache and only do this at an interval.  
    ConfigurationManager.RefreshSection("appSettings");  
    var index = ConfigurationManager.AppSettings["UseSolr"] as string;  

    bool result;

    bool.TryParse(index, out result);  

    return result;
}