IoC with values ​​of types and dependencies of type of object

I am looking for suggestions on the best way to design objects for IoC

Suppose I have an object (Service) that has a dependency on a DataContext that is registered in Ioc.

But this also requires the name property, I could create an object like this:

class Service { public Service(IDataContext dataContext, string name) { this._dataContext = dataContext; this._name = name } public string Name { get { return _name; } } } 

The problem is that with Ioc containers it is very difficult to use as a string object, such as a name, it is not easy to register and use is complicated with the Ioc container: Therefore, the resolution becomes confusing:

 var service = Ioc.Resolve<Service>( ?? ) 

Another approach is to develop it as follows:

 class Service { public Service(IDataContext dataContext) { this._dataContext = dataContext; } public string Name { get; set; } } 

Now the resolution is simpler:

 var service = Ioc.Resolve<Service>(); service.Name = "Some name"; 

A single downsite indicates that the name is no longer required. I would like to hear from DI or IoC experts how they will design this and still remain agnostic enough for a specific Ioc container technology.

I know that a lot depends on how you want to use this, option 2 would be ideal if the name were really optional. But in the case where a name is required, you can add a verification step at another point in the code, but rather go to a project to make Ioc easier.

Thoughts?

+8
c # dependency-injection inversion-of-control constructor-injection
source share
5 answers

Your choice of a DI container should not dictate the design of your API. If name is not optional, it must be part of the constructor signature (which makes it mandatory).

The next question is how to set up a container without gaining tons of overhead. How to do this depends on the container. Here's how to implement the convention around a string argument in Castle Windsor:

 public class NameConvention : ISubDependencyResolver { public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency) { return dependency.TargetType == typeof(string) && dependency.DependencyKey == "name"; } public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency) { return "foo"; // use whatever value you'd like, // or derive it from the provided models } } 

Then register the NameConvention with the container as follows:

 container.Kernel.Resolver.AddSubResolver(new NameConvention()); 

If your container does not have the appropriate extensibility points, select the container that does.

+4
source share

The problem is that it is very difficult to use with Ioc containers since a string object such as a name is not easy to register and use is complicated with the Ioc container

The best IoC containers will provide easy ways to provide constructor arguments when setting up.

Your first injection constructor example is usually considered the preferred way. Think of your constructor as a contract that, once executed, displays the actual object.

A second injection of code sample is generally considered less preferable for constructor injection. In any case, IoC containers usually provide you with the ability to provide parameter values ​​or constructor properties in the configuration, which will be provided each time you ask your IoC to create this object for you.

I'm not sure which IoC container you are going to use, but here is a sample code used to configure StructureMap and provides string values ​​for various services. If I misunderstand your question, this is similar to what you want to do.

 ObjectFactory.Initialize(x => { x.For<ICatalogAdminService>().Use<DLinkCatalogAdminService>() .Ctor<string>("catalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString) .Ctor<string>("contentConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString) .Ctor<string>("webCatalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_WebConnectionString"].ConnectionString) .Ctor<string>("dlinkPromotionAdminConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString); x.For<IContentManagementAdminService>().Use<DLinkContentManagementAdminService>() .Ctor<string>("contentConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString) .Ctor<string>("webCatalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_WebConnectionString"].ConnectionString) .Ctor<string>("dlinkPromotionConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString); x.For<IPromotionAdminService>().Use<DLinkPromotionAdminService>() .Ctor<string>("catalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString) .Ctor<string>("promotionConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString); x.For<ISearchService>().Use<Extractor>(); x.For<IImporter>().Use<Importer>(); x.For<IOrderAdminService>().Use<DLinkOrderAdminService>() .Ctor<string>("contentConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString) .Ctor<string>("orderConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_OrdersConnectionString"].ConnectionString); }); 

EDIT

Responding to the comment, if you want to manually specify the constructor argument, it will look like this:

 ObjectFactory.GetInstance<ICatalogAdminService>(new ExplicitArguments( new Dictionary<string, object>() { { "parameter1", "someValue" } })); 

Obviously this can get ugly fast, so you might want to hack some factory / helper methods if you do this often.

+3
source share

The approach that I usually do in this situation is to inject an object of settings instead of a string, and then a query in the constructor for a property representing this string. Or in some cases it’s even better when I need this property of the string, I took it from these settings so that it can be changed (useful if this is really a program setting).

Another option is to use something like anchor annotation. I don’t know which dependency injection infrastructure you are using, but here is how to do it in the guice (java) framework, which I am currently working on.

+2
source share

If you use Castle Windsor, you can use typed factories, which you can read about here here . In fact, typed factories allow you to create an interface that looks like this.

 public interface IServiceFactory { IService Create(string name); } 

By entering this and calling Create() with the name of your choice, Windsor will return the constructed implementation of IService .

+1
source share

Create it the way you always have, taking into account good technical techniques (SOLID, etc.). Then, if your container of choice limits you, you are either not using it correctly, or you are using the wrong container.

In Windsor, you can easily provide built-in, hard-coded component dependencies during registration:

 container.Register(Component.For<Foo>().DependsOn(new{name = "Stefan"}); 

You can also provide more dynamic dependencies or depend on the values ​​from your XML configuration if you need to change them after compilation.

If the value is optional, make it optional using the constructor, which sets the default value for it, using constructor overloads, or as a property. Again, a good container will handle all of these cases if the one you are currently using cannot switch to the best.

+1
source share

All Articles