I'm just starting with Unity IOC, hoping someone will help. I need to be able to switch the dependencies in Unity at run time. I have two containers each for production and dev/test environments, "prodRepository" and "testRepository" defined in the web.config as follows:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="TestDataService" type="MyApp.API.Data.TestDataRepository.TestDataService, MyApp.API.Data" />
<alias alias="ProdDataService" type="MyApp.API.Data.ProdDataRepository.ProdDataService, MyApp.API.Data" />
<assembly name="MyApp.API.Data" />
<container name="testRepository">
<register type="MyApp.API.Data.IDataService" mapTo="TestDataService">
<lifetime type="hierarchical" />
</register>
</container>
<container name="prodRepository">
<register type="MyApp.API.Data.IDataService" mapTo="ProdDataService">
<lifetime type="hierarchical" />
</register>
</container>
</unity>
In the WebApiConfig class the Unit is configured as follows
public static void Register(HttpConfiguration config)
{
config.DependencyResolver = RegisterUnity("prodRepository");
//... api configuration ommitted for illustration
}
public static IDependencyResolver RegisterUnity(string containerName)
{
var container = new UnityContainer();
container.LoadConfiguration(containerName);
return new UnityResolver(container);
}
Just for test I created a simple controller and action to switch the configuration:
[HttpGet]
public IHttpActionResult SwitchResolver(string rName)
{
GlobalConfiguration.Configuration
.DependencyResolver = WebApiConfig.RegisterUnity(rName);
return Ok();
}
and I call it from a web browser:
http://localhost/MyApp/api/Test/SwitchResolver?rName=prodRepository
When I try to retrieve the actual data from the repositories via the API, at first it comes from "prodRepository", understandably, as that's how it is initialized in the code. After I switch it to "testRepository" from the browser, the data comes from the test repo as expected. When I switch it back to prodRepository, the API keeps sending me the data from the test repo.
I see in the controller that the GlobalConfiguration.Configuration .DependencyResolver changes the container and registrations to the ones specified in the URL query as expected, but it seems to change the configuration only once then stays at that configuration.
Ok, so this evil plan is what I came up with but as I am new to this I am probably going wrong direction altogether.
I need to be able to specify dynamically at run-time which container to use, hopefully without reloading the API. Does the above code make sense or what would you suggest?
It looks like you are going awry in many ways:
Using XML to configure a DI container is considered to be an obsolete approach.
Do you really want to access test data from your production environment and vice versa? Usually one environment is chosen through a configuration setting and the setting itself is changed upon deployment to each environment. And in that case, it makes sense to load the data service only 1 time at application startup.
If the answer to #2 is no, one way to get the job done easily and reliably is to use web.config transforms during deployment.
If the answer to #2 is yes, you can solve this by using a strategy pattern, which allows you to create all data services at startup and switch between them at runtime.
Here is an example of #4:
NOTE: WebApi is stateless. It doesn't store anything on the server after the request has ended. Furthermore, if your WebApi client is not a browser, you may not be able to use techniques such as session state to store which data provider you are accessing from one request to the next because this depends on cookies.
Therefore, having a SwitchResolver action is probably nonsensical. You should provide the repository on each request or otherwise have a default repository that can be overridden with a parameter per request.
Interfaces
public interface IDataService
{
void DoSomething();
bool AppliesTo(string provider);
}
public interface IDataServiceStrategy
{
void DoSomething(string provider);
}
Data Services
public class TestDataService : IDataService
{
public void DoSomething()
{
// Implementation
}
public bool AppliesTo(string provider)
{
return provider.Equals("testRepository");
}
}
public class ProdDataService : IDataService
{
public void DoSomething()
{
// Implementation
}
public bool AppliesTo(string provider)
{
return provider.Equals("prodRepository");
}
}
Strategy
This is the class that does all of the heavy lifting.
There is a GetDataService method that returns the selected service based on the passed in string. Note that you could alternatively make this method public in order to return an instance of IDataService to your controller so you wouldn't have to make two implementations of DoSomething.
public class DataServiceStrategy
{
private readonly IDataService[] dataServices;
public DataServiceStrategy(IDataService[] dataServices)
{
if (dataServices == null)
throw new ArgumentNullException("dataServices");
this.dataServices = dataServices;
}
public void DoSomething(string provider)
{
var dataService = this.GetDataService(provider);
dataService.DoSomething();
}
private IDataService GetDataService(string provider)
{
var dataService = this.dataServices.Where(ds => ds.AppliesTo(provider));
if (dataService == null)
{
// Note: you could alternatively use a default provider here
// by passing another parameter through the constructor
throw new InvalidOperationException("Provider '" + provider + "' not registered.");
}
return dataService;
}
}
See these alternate implementations for some inspiration:
Best way to use StructureMap to implement Strategy pattern
Factory method with DI and Ioc
Unity Registration
Here we register the services with Unity using a container extension rather than XML configuration.
You should also ensure you are using the correct way to register Unity with WebApi as per MSDN.
public static IDependencyResolver RegisterUnity(string containerName)
{
var container = new UnityContainer();
container.AddNewExtension<MyContainerExtension>();
return new UnityResolver(container);
}
public class MyContainerExtension
: UnityContainerExtension
{
protected override void Initialize()
{
// Register data services
// Important: In Unity you must give types a name in order to resolve an array of types
this.Container.RegisterType<IDataService, TestDataService>("TestDataService");
this.Container.RegisterType<IDataService, ProdDataService>("ProdDataService");
// Register strategy
this.Container.RegisterType<IDataServiceStrategy, DataServiceStrategy>(
new InjectionConstructor(new ResolvedParameter<IDataService[]>()));
}
}
Usage
public class SomeController : ApiController
{
private readonly IDataServiceStrategy dataServiceStrategy;
public SomeController(IDataServiceStrategy dataServiceStrategy)
{
if (dataServiceStrategy == null)
throw new ArgumentNullException("dataServiceStrategy");
this.dataServiceStrategy = dataServiceStrategy;
}
// Valid values for rName are "prodRepository" or "testRepository"
[HttpGet]
public IHttpActionResult DoSomething(string rName)
{
this.dataServiceStrategy.DoSomething(rName);
return Ok();
}
}
I highly recommend you read the book Dependency Injection in .NET by Mark Seemann. It will help lead you down the correct path and help you make the best choices for your application as they apply to DI, which is more than what I can answer on a single question on SO.
Related
I'm facing a problem trying to implement a unit test for a method on a service.
The architecture of the project is a little bit cumbersome, to say the less...
The problem is that within the method to test it calls another method to take an instance of another service, here is the little monster:
public void SendOrderEmail(string orderCode)
{
Order order= GetOrderService().SerachByCode(orderCode);
.... Send email with the order ....
}
private IOrderService GetOrderService()
{
return OrderService = AutofacDependencyResolver.Current.ApplicationContainer.Resolve<IOrderService>();
}
Please, don't ask why a service calls another service or why is that service not injected at the constructor, as i said the architecture of this project is weird in some points.
I just need to know what is the way to implement a unit test for a method like that.
Thank you!
I would refactor a little the code, let the class that implement this method have IOrderService injected through the constructor, save the instance and then use it,
this way you can inject your fake IOrderService during the test (or use Automock) :)
If you really really can't change the constructor, you can use a property to set IOrderService
---------------- edit
Since i got some downvote on this answer I've tried to get to understand better what is going on.
I'm not sure about this, but seems like you can't edit this class you wrote about, you just want to test it.
Well if that is the case i think i can still give you some advices.
Advice number one: make a test project, link the class file, make a new file with a class like the following one.
class AutofacDependencyResolver {
public static Current { get; private set; }
public ILifetimeScope ApplicationContainer { get; private set; }
public AutofacDependencyResolver(ILifetimeScope scope) {
Current = this;
ApplicationContainer = scope;
}
}
Since the class you need to test is linked it's gonne to compile it and you just can now achieve what you need.
The other (and i think better) advice is do not test stuff you did not wrote / can't modify. What i'm suggesting is writing an adapter, so a class that use the one you can't modify as a black box.
In this case i think you need to test the email, so just check the email output the address stuff like that and ignore the rest.
the people who wrote those classes should have followed solid principles...
As others have said, and you're probably aware yourself anyway, you really want to refactor classes like this and use constructor injection if at all possible. Service location is generally considered an anti-pattern (https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) and it specifically makes unit testing like this harder and less transparent.
However, if you absolutely can't refactor, you can still make methods like this somewhat testable by just providing different registrations for the services you're accessing via service location.
In your case, if you have:
public class EmailSender
{
public void SendOrderEmail(string orderCode)
{
Order order = GetOrderService().SearchByCode(orderCode);
//....Send email with the order ....
}
private IOrderService GetOrderService()
{
return AutofacDependencyResolver.Current.ApplicationContainer.Resolve<IOrderService>();
}
}
...and you're looking to specifically run unit tests over SendOrderEmail to validate the logic surrounding your IOrderService implementation (which could be easily covered by a separate test), the other classes implied there might look like:
public class AutofacDependencyResolver // this is problematic but we can't change it
{
public AutofacDependencyResolver(IContainer applicationContainer)
{
ApplicationContainer = applicationContainer;
}
public IContainer ApplicationContainer { get; }
public static AutofacDependencyResolver Current { get; private set; }
public static void SetContainer(IContainer container)
{
Current = new AutofacDependencyResolver(container);
}
}
public static class ContainerProvider // this sets up production config across your app
{
public static IContainer GetProductionContainer()
{
var builder = new ContainerBuilder();
builder.RegisterType<RealOrderService>()
.As<IOrderService>();
// register all other real dependencies here
return builder.Build();
}
}
With that setup, you only need to provide mocks which are required for the specific method you're testing, assuming you can set your container within AutofacDependencyResolver easily in order to have production and test configuration running in parallel. That might look like the following, using xUnit, Moq and Autofac in a test project:
public class EmailSenderTests
{
private readonly Mock<IOrderService> _orderService;
public EmailSenderTests()
{
// to set up the test fixture we'll create a mock OrderService and store a reference to the mock itself for validation later on
_orderService = new Mock<IOrderService>();
var mockOrder = new Order();
_orderService.Setup(os => os.SearchByCode(It.IsAny<string>()))
.Returns(mockOrder);
}
private IContainer GetTestContainer()
{
// here we're adding just one registration we need, setting the mocked OrderService instance to be used for IOrderService
var builder = new ContainerBuilder();
builder.Register(c => _orderService.Object)
.As<IOrderService>();
return builder.Build();
}
[Fact]
public void SendEmail()
{
AutofacDependencyResolver.SetContainer(GetTestContainer()); // set the test container on the global singleton
var sender = new EmailSender();
sender.SendOrderEmail("abc"); // internally the email sender will retrieve the mock IOrderService via service location
// make any assertions here, e.g.
_orderService.Verify(os=>os.SearchByCode("abc"), Times.Exactly(1));
}
}
Our existing database deployment has a single 'master' and a read-only replica. Using ASP.NET's Web API2 and an IoC container I want to create controller actions whose attribute (or lack there of) indicate which database connection is to be used for that request (See Controller and Services usage below)...
public MyController : ApiController
{
public MyController(IService1 service1, IService2 service2) { ... }
// this action just needs the read only connection
// so no special attribute is present
public Foo GetFoo(int id)
{
var foo = this.service1.GetFoo(id);
this.service2.GetSubFoo(foo);
return foo;
}
// This attribute indicates a readwrite db connection is needed
[ReadWrteNeeded]
public Foo PostFoo(Foo foo)
{
var newFoo = this.service1.CreateFoo(foo);
return newFoo;
}
}
public Service1 : IService1
{
// The dbSession instance injected here will be
// based off of the action invoked for this request
public Service1(IDbSession dbSession) { ... }
public Foo GetFoo(int id)
{
return this.dbSession.Query<Foo>(...);
}
public Foo CreateFoo(Foo newFoo)
{
this.dbSession.Insert<Foo>(newFoo);
return newFoo;
}
}
I know how to setup my IoC (structuremap or Autofac) to handle per request IDbSession instances.
However, I'm not sure how I would go about making the type of IDbSession instance for the request to key off the indicator attribute (or lack there of) on the matching controller's action. I assume I will need to create an ActionFilter that will look for the indicator attribute and with that information identify, or create, the correct type of IDbSession (read-only or read-write). But how do I make sure that the created IDbSession's lifecycle is managed by the container? You don't inject instances into the container at runtime, that would be silly. I know Filters are created once at startup (making them singleton-ish) so I can't inject a value into the Filter's ctor.
I thought about creating an IDbSessionFactory that would have 'CreateReadOnlyDbSession' and 'CreateReadWriteDbSession' interfaces, but don't I need the IoC container (and its framework) to create the instance otherwise it can't manage its lifecycle (call dispose when the http request is complete).
Thoughts?
PS During development, I have just been creating a ReadWrite connection for every action, but I really want to avoid that long-term. I could also split out the Services methods into separate read-only and read-write classes, but I'd like to avoid that as well as placing GetFoo and WriteFoo in two different Service implementations just seems a bit wonky.
UPDATE:
I started to use Steven's suggestion of making a DbSessionProxy. That worked, but I was really looking for a pure IoC solution. Having to use HttpContext and/or (in my case) Request.Properties just felt a bit dirty to me. So, if I had to get dirty, I might as well go all the way, right?
For IoC I used Structuremap and WebApi.Structuremap. The latter package sets up a nested container per Http Request plus it allows you to inject the current HttpRequestMessage into a Service (this is important). Here's what I did...
IoC Container Setup:
For<IDbSession>().Use(() => DbSession.ReadOnly()).Named("ReadOnly");
For<IDbSession>().Use(() => DbSession.ReadWrite()).Named("ReadWrite");
For<ISampleService>().Use<SampleService>();
DbAccessAttribute (ActionFilter):
public class DbAccessAttribute : ActionFilterAttribute
{
private readonly DbSessionType dbType;
public DbAccessAttribute(DbSessionType dbType)
{
this.dbType = dbType;
}
public override bool AllowMultiple => false;
public override void OnActionExecuting(HttpActionContext actionContext)
{
var container = (IContainer)actionContext.GetService<IContainer>();
var dbSession = this.dbType == DbSessionType.ReadOnly ?
container.GetInstance<IDbSession>("ReadOnly") :
container.GetInstance<IDbSession>("ReadWrite");
// if this is a ReadWrite HttpRequest start an Request long
// database transaction
if (this.dbType == DbSessionType.ReadWrite)
{
dbSession.Begin();
}
actionContext.Request.Properties["DbSession"] = dbSession;
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var dbSession = (IDbSession)actionExecutedContext.Request.Properties["DbSession"];
if (this.dbType == DbSessionType.ReadWrite)
{
// if we are responding with 'success' commit otherwise rollback
if (actionExecutedContext.Response != null &&
actionExecutedContext.Response.IsSuccessStatusCode &&
actionExecutedContext.Exception == null)
{
dbSession.Commit();
}
else
{
dbSession.Rollback();
}
}
}
}
Updated Service1:
public class Service1: IService1
{
private readonly HttpRequestMessage request;
private IDbSession dbSession;
public SampleService(HttpRequestMessage request)
{
// WARNING: Never attempt to access request.Properties[Constants.RequestProperty.DbSession]
// in the ctor, it won't be set yet.
this.request = request;
}
private IDbSession Db => (IDbSession)request.Properties["DbSession"];
public Foo GetFoo(int id)
{
return this.Db.Query<Foo>(...);
}
public Foo CreateFoo(Foo newFoo)
{
this.Db.Insert<Foo>(newFoo);
return newFoo;
}
}
I assume I will need to create an ActionFilter that will look for the indicator attribute and with that information identify, or create, the correct type of IDbSession (read-only or read-write).
With your current design, I would say an ActionFilter is the way to go. I do think however that a different design would serve you better, which is one where business operations are more explicitly modelled behind a generic abstraction, since you can in that case place the attribute in the business operation, and when you explicitly separate read operations from write operations (CQS/CQRS), you might not even need this attribute at all. But I'll consider this out of scope of your question right now, so that means an ActionFilter is the the way to go for you.
But how do I make sure that the created IDbSession's lifecycle is managed by the container?
The trick is let the ActionFilter store information about which database to use in a request-global value. This allows you to create a proxy implementation for IDbSession that is able to switch between a readable and writable implementation internally, based on this setting.
For instance:
public class ReadWriteSwitchableDbSessionProxy : IDbSession
{
private readonly IDbSession reader;
private readonly IDbSession writer;
public ReadWriteSwitchableDbSessionProxy(
IDbSession reader, IDbSession writer) { ... }
// Session operations
public IQueryable<T> Set<T>() => this.CurrentSession.Set<T>();
private IDbSession CurrentSession
{
get
{
var write = (bool)HttpContext.Current.Items["WritableSession"];
return write ? this.writer : this.reader;
}
}
}
Within my Web API I have linked Autofac as IoC container, and I do it like this:
Domain level
public class Autofac
{
protected ContainerBuilder Builder { get; set; }
public Autofac()
{
this.Builder = new ContainerBuilder();
}
public virtual IContainer Register()
{
// Register dependencies
SetUpRegistration(this.Builder);
// Build registration.
var container = this.Builder.Build();
// End
return container;
}
private static void SetUpRegistration(ContainerBuilder builder)
{
// === DATALAYER === //
// MyRepository
builder.RegisterType<MyRepository>()
.As<IMyRepository>()
.InstancePerLifetimeScope();
// === DOMAIN === //
// MyManager
builder.RegisterType<MyManager>()
.As<IMyManager>()
.InstancePerLifetimeScope();
}
}
Web API
public class Autofac : Domain.IoC.Autofac
{
public IContainer Register(HttpConfiguration config)
{
// Register your Web API controllers.
base.Builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
base.Builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
// Complete registration and get container instance.
var container = base.Register();
// Set the dependency resolver to be Autofac.
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// Done.
return container;
}
}
As you see it inherits from the base class from Domain and sets up Web API specific config.
Usage
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
new IoC.Autofac().Register(GlobalConfiguration.Configuration);
}
Which is at global.asax, as you know.
The question
This works fine for Web API, but I haven't got a clue what I need to do to register all this within a UnitTest project context.
The idea is that I would create a similar implementation to the Autofac class at Web API level, but than with mocks (completely ignoring the base class from Domain).
Any pointers?
Personally I never see the need (and I struggle to comprehend how viable or helpful it would be) to setup my IoC container directly within a unit test.
As a unit test is used to test a logical piece of code that can be quickly built, easily ran and doesn't require much (I'd advocate no) tear-down. It should not require all of your application to be be setup for the test to run.
Remember that your unit test is simply testing the flow of data through the system i.e that your DomainManager is actually going to call a IRepository when you expect that it should. Then you would have separate test classes for all your repositories to determine that they would correctly add to the database etc.
I'm not sure how you use the DBContext class but as an example of a wrapper this is what it would sort of look like.
interface IDBSetWrapper
{
object Add(object entity);
}
interface IDBContextWrapper
{
...
IDBSet Set(Type entityType);
...
}
class DBContextWrapper : IDBContextWrapper
{
private readonly DBContext context;
public DBContextWrapper()
{
context = new DBContext();
}
...
public IDBSet Set(Type entityType)
{
var dbSet = context.Set(entityType);
return new DBSetWrapper(dbSet);
}
...
}
It's not much but I hope that it demonstrates what I mean about a thin wrapper. Basically the wrapper is the DBContext and will contain an instance of it within the class, the actual DBContext will be called when you request the wrapper to do anything.
I have shown what would happen when returning another object (in this case a DBSet), this will also be wrapped in a separate object with an interface. This is so that you can mock the returns from this class easily.
You can add this new wrapper into your IoC a little better now as it provides an interface.
One thing to note is that you won't be able to and probably wouldn't wish to test the wrapper class, there would be very little point as I see it. But previously I've seen colleagues do an integration test on these sort of classes.
I'm using Drum which provides a generic class `UriMaker:
public class UriMaker<TController>
{
// I need use this one
public UriMaker(UriMakerContext context, HttpRequestMessage request) { }
public UriMaker(Func<MethodInfo, RouteEntry> mapper, UrlHelper urlHelper) { }
}
Used like this:
public class UserController : ApiController
{
public UserController(UriMaker<UserController> urlMaker) {}
}
I've used to register it with Unity:
container.RegisterType(typeof(UriMaker<>),
new InjectionConstructor(typeof(UriMakerContext), typeof(HttpRequestMessage)));
but now migrating to Simple Injector. I already have this:
UriMakerContext uriMaker = config.MapHttpAttributeRoutesAndUseUriMaker();
container.RegisterSingle(uriMakerContext);
So how now register UriMaker<> itself?
Although it is possible to configure Simple Injector to allow injecting an UriMaker<TController> directly into your controllers, I strongly advice against this for multiple reasons.
First of all, you should strive to minimize the dependencies your application takes on external libraries. This can easily be done by defining an application specific abstraction (conforming the ISP).
Second, injecting the UriMaker directly makes your extremely hard to test, since the UriMaker is pulled into your test code, while it assumes an active HTTP request and assumes the Web API route system to be configured correctly. These are all things you don't want your test code to be dependent upon.
Last, it makes verifying the object graph harder, since the UriMaker depends on an HttpRequestMessage, which is a runtime value. In general, runtime values should not be injected into the constructors of your services. You should build up your object graph with components (the stuff that contains the application's behavior) and you send runtime data through the object graph after construction.
So instead, I suggest the following abstraction:
public interface IUrlProvider
{
Uri UriFor<TController>(Expression<Action<TController>> action);
}
Now your controllers can depend on this IUrlProvider instead of depending on an external library:
public class UserController : ApiController
{
private readonly IUrlProvider urlProvider;
public UserController(IUrlProvider urlProvider)
{
this.urlProvider = urlProvider;
}
public string Get()
{
this.urlProvider.UriFor<HomeController>(c => c.SomeFancyAction());
}
}
Under the covers you of course still need to call Drum, and for this you need to define a proxy implementation for IUrlProvider:
public class DrumUrlProvider : IUrlProvider
{
private readonly UriMakerContext context;
private readonly Func<HttpRequestMessage> messageProvider;
public DrumUrlProvider(UriMakerContext context,
Func<HttpRequestMessage> messageProvider)
{
this.context = context;
this.messageProvider= messageProvider;
}
public Uri UriFor<TController>(Expression<Action<TController>> action)
{
HttpRequestMessage message = this.messageProvider.Invoke();
var maker = new UriMaker<TController>(this.context, message);
return maker.UriFor(action);
}
}
This implementation can be registered as singleton in the following way:
container.EnableHttpRequestMessageTracking(config);
UriMakerContext uriMakerContext =
config.MapHttpAttributeRoutesAndUseUriMaker();
IUrlProvider drumProvider = new DrumUrlProvider(uriMakerContext,
() => container.GetCurrentHttpRequestMessage());
container.RegisterSingle<IUrlProvider>(drumProvider);
This example uses the Simple Injector Web API integration package to allow retrieving the current request's HttpRequestMessage using the EnableHttpRequestMessageTracking and GetCurrentHttpRequestMessage extension methods as explained here.
I have 3 service components, one low-level service responsible for some kind of data serialization, one in the middle responsible for coordinating saves/loads, and one MVC Controller responsible for API publication.
Each of the 3 components logically refers to the other "below" it. The middle service has another parameter, which is known at runtime, based on request data. From this 3 components the controller and the middle service are represented by classes (doesn't make sense to introduce interfaces because nothing to mock), and the lowest level is repesented by an interface, making it available to unit-test the middle service or the controller. I'd like to use DI (specifically Ninject) to build my controller class. My question is if any kind of best practice exists for handling this scenario. Currently I see two way of implementation. (The validations, proper implementations are ommitted for the clarity.)
First of all, here is a sample implementation of the middle service and the lower level interface.
public interface ISerializer {
void Serialize(object data);
}
public class MyService {
private string _dataId;
private ISerializer _serializer;
public MyService(string dataId, ISerializer serializer) {
_serializer = serializer;
_dataId = dataId;
}
public bool CanProcess(MyDTO data) {
...
}
public void DoSomeProcessing(MyDTO data) {
...
}
}
Version 1: inject the whole middle service to the controller as a factory
public class MyController : Controller {
private Func<string, MyService> _myServiceFactory;
public MyController(Func<string, MyService> myServiceFactory) {
_myServiceFactory = myServiceFactory;
}
...
[HttpPost]
public JsonResult Process(string dataId, MyDTO model) {
using (var myService = _myServiceFactory(dataId)) {
...
if (myService.CanProcess(model))
myService.DoSomeProcessing(model);
...
return Json("ok");
}
}
}
Version 2: Injecting directly the lower-level interface to the controller, and instantiate the middle service "manually".
public class MyController : Controller {
private ISerializer _serializer;
public MyController(ISerializer serializer) {
_serializer = serializer;
}
...
[HttpPost]
public JsonResult Process(string dataId, MyDTO model) {
using (var myService = new MyService(dataId, _serializer) {
...
if (myService.CanProcess(model))
myService.DoSomeProcessing(model);
...
return Json("ok");
}
}
}
Which one is more proper, or should I choose a completely different solution?
First of all, I like my services to be stateless, so I don't like the idea of passing dataId
to the service's constructor. When services are stateless they are safer. You can call their methods not worrying if they are currently in a valid state. It also makes it easier to test and mock them. You can also reduce the amount of used memory, as you only need one instance of a stateless service.
If you moved dataId to DoSomeProcessing as a parameter you would be able to easily instantiate MyService with Ninject and the proper implementation of ISerializer would be injected automatically.
However if you insist on passing it to the constructor "Version 1" is quite close to what I'd consider good. Factory is a nice trick to let DI inject dependencies, when there are also data parameters needed in the constructor. I would inject MyServiceFactory to the controller. I'd create another class for it:
public class MyServiceFactory : IMyServiceFactory // an interface to me able to mock it if needed
{
ISerializer _serializer;
MyServiceFactory(ISerializer serializer){ // here Ninject can inject the dependency
_serializer = serializer;
}
IMyService Create(int dataId){ // here you can pass additional parameter
return new MyService(dataId, _serializer);
}
}
This way you can easily avoid hard dependencies and make the code more maintainable and more testable.
"Version 2" is wrong. If you ever want to test your controller or replace MyService with another implementation - you are stuck. You'll have to do a lot of tedious refactoring (depending on the amount of usages). And finally you'll end up with something similar to what I suggested above. :)