I have an application which uses an interface IBackend
for communicating with a backend. In the production environment I wish to use class ProdBackend : IBackend
as the implementation of the interface. In the test environment I wish to use TestBackend : IBackend
.
The application is packaged into a zip file, which must be independent of whether it is deployed in the prod or test environment.
How can I make the application use a different implementation of IBackend
depending on the environment it is deployed in?
Can I do this by simply having to different .dll's installed in the two environments and naming the classes the same?
UPDATE 11:12 - 15/1:
The packaged application is not allowed to include the prod implementation, i.e. ProdBackend : IBackend
. So the application does not know ProdBackend : IBackend
at compile time.
Supplying different DLL's with different implementations of the same interfaces would work fine.
An more elegant solution would be to do this but to add a Service Locator that supplies the implementation you need. Then you could configure the Service Locator to return an implementation based on the platform you run on.
There are many service locators out there, but a simple one that runs on all platforms supported by Mono is TinyIOC
There are many ways to do this depending on your set up. I can think of these:
- Machine Name: If you know the server name (or convention) for the testing server, you can new up the dependecy based on
Envirnoment.MachineName
.
- Plugin: If you use a plugin model (by building an assembly that contains
ProdBackend
and one the contains TestBackend
, you can make the decision when deploying the application. You can do this by having a plugin directory that your IOC container will use to wire up dependencies (or other means).
- Configuration: You can use a value in a configuration file, to determine the environment and then use that information to pick between the two implementations. Then, when deploying to the prod environment, you can adjust the configuration file (
app.config
) accordingly.
The entire point of the Test environment is to allow you to be as reasonably sure as possible that when you deploy something to the Production environment it will work properly. If you have one set of code that executes on Test and a different set of code that executes when deployed to Production, how are you accomplishing that goal?
Essentially, the difference between Test and Production from a code perspective should should be configuration only (different db connection, possibly more verbose logging on Test, etc). Otherwise you are, essentially, deploying untested code to Production which is, in my opinion, sowing the seeds for possible disaster.
There is a concept: Contextual Binding — that is implemented by almost all dependency injection container. In practice this mean, that you could add the condition to your bindings. Here is example using ninject
Define 'production' environment condition
For example by Environment.MachineName
, or any other suitable for you:
private static readonly Func<bool> IsCurrentEnvironmentProduction =
() => Environment.MachineName == "Production.Server";
Define bindings for different environments
public static IKernel InitializeKernel()
{
var kernel = new StandardKernel();
// binding for production
kernel
.Bind<IBackend>()
.To<ProdBackend>()
.When(request => IsCurrentEnvironmentProduction());
// binding for test environment
kernel
.Bind<IBackend>()
.To<TestBackend>()
.When(request => !IsCurrentEnvironmentProduction());
return kernel;
}
Resolve instance depends on environment
DI container will do all for you
var backend = kernel.Get<IBackend>();
Full sample is available here
Remark about Mono
According to Ninject download there is a release for Mono, but you could implement same functionality in other DI containers also