0

I have a an ASP.NET MVC 5 app, I am using Structuremap 3 as the IOC.

I have an Interface which is used in multiple types through the N-Tier and I cannot find a succinct explanation of how to to map multiple types to the same interface for automatic constructor injection.

For example I have the following declarations in my registry

For<IDataContextAsync>().Use<DbGeoContext>();  
For<IDataContextAsync>().Use<DbEspContext>();

When I run my app I understand the last added instance of IDataContextAsync will be the default instance used when my app creates an instance of a type that needs an IDataContextAsync as a parameter in the constructor.

So my question is how do I tell Structuremap 3 to use a specific instance of IDataContextAsync in the registry relevant to the type being created?

Thanks.

Csharper
  • 53
  • 2
  • 10

1 Answers1

1

In order to distinguish between different implementations, you can use named instances (see documentation here). You register a named instance like this:

//Default instance
For<IDataContextAsync>().Use<DbGeoContext>();  
For<IDataContextAsync>().Add<DbEspContext>().Named("EspContext");

Note that the Add method registers an instance without setting it as the default, while the Use method registers it as the default instance. As you've already realized, the last instance registered with the Use method becomes the default instance.

In order to tell a dependee which instance should passed to the constructor, you can use the Ctor instruction to specify constructor parameters(see documentation here):

For<IMyRepository>().Use<MyRepository>()
    .Ctor<IDataContextAsync>().Named("EspContext");

which is a confusingly named shorthand for:

For<IMyRepository>().Use<MyRepository>()
    .Ctor<IDataContextAsync>()
    .Is(c => c.GetInstance<IDataContextAsync>("EspContext"));

Alternatively, you can skip the named parameters magic and just go straight for the concrete type with:

For<IMyRepository>().Use<MyRepository>()
    .Ctor<IDataContextAsync>().Is<DbEspContext>();
PHeiberg
  • 29,411
  • 6
  • 59
  • 81
  • Hi thanks, for the reply, I had figured it out in the end but it took me a while. I would mark your answer but I don't have enough points yet. – Csharper Dec 09 '15 at 20:08
  • Actually I have spotted an issue, on application start I retrieve the instance of EspContext and update its connection string, but when I trace the request down to the Repository layer the connection string hasn't changed? Hmm... Is there another method than GetInstance which will do the same thing but without creating an Instance? – Csharper Dec 09 '15 at 23:05
  • Wouldn't it be easier to set the connection string to the correct value from the beginning? If you don't know the value until a specific point in time in the application life cycle, use a factory pattern and inject a factory that can produce the connection instead of injecting the actual connection. I think that your problem is that it's not the same instance that comes to the repository as the one you change. You can set the [life cycle](http://structuremap.github.io/object-lifecycle/supported-lifecycles/) for each type in structuremap. The default is transient (new object per object graph). – PHeiberg Dec 10 '15 at 06:12
  • Hi thanks for the reply, the plot thickens.. I have used the For().Use() .Ctor().Is(); method but the wrong DbContext is being injected, could you provide a sample that shows this method working with 2 dbcontexts? Thanks – Csharper Dec 11 '15 at 07:24
  • Sure, [here's a gist](https://gist.github.com/pheiberg/a96f1411ae1df6300cb9). I ran it succesfully with Structuremap v2, 3 and 4. – PHeiberg Dec 11 '15 at 16:28
  • Thanks, I see the issue now, I also have a IUnitOfWorkAsync which has only one declaration in the structure map config so it was using the Default IDataContextAsync which was the wrong DataContext. Many thanks for your assistance. – Csharper Dec 11 '15 at 16:53