12

This is what I want from DI container:

public class Class
{
   public Class(IDependency dependency, string data)  { }
}

var obj = di.Resolve<Class>(() => new Class(null, "test"));

Points of interest:

  1. Can resolve both dependency and data in constructor.
  2. Can use type-safe syntax to pass constructor parameters (exact syntax may vary). Yes I can do it myself by getting constructor arguments from (Expression.Body as NewExpression) - but I'll need a way to detect what arguments are registered in the container.

Another major requirements is that I'd like my components to be automatically picked up, i.e. I don't want to register Class - I want IoC to pick it up because it knows how to resolve IDependency.

Also, Property Injection can be useful sometimes, but this is optional.

The question is really about the combination of features - to have all of them - type-safe, parameters, automatic pick-up... It's easy to check one feature, but a combination of them is not easy to verify unless one's familiar with particular container and knows its features. Thus the question.

Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
queen3
  • 15,333
  • 8
  • 64
  • 119
  • I don't think there's any container that supports that kind of syntax. But many (almost all) containers support explicit container parameters, and probably with cleaner syntax. – R. Martinho Fernandes Nov 06 '09 at 10:42
  • "Probably" is not the answer... as far as I know I can pass object[] array of parameters but it is obviously not type-safe... I can change parameters order and won't know about this until runtime. – queen3 Nov 06 '09 at 10:46
  • By cleaner I meant that you don't need to explicitly use the ctor in your code, like you do above. Instead of what you wrote, why not: `var obj = new Class(di.Resolve(), "test");`? – R. Martinho Fernandes Nov 06 '09 at 10:51
  • Because Class is parameter of my Controller constructor in MVC and Controller is created by DI container. – queen3 Nov 06 '09 at 10:54
  • Hm, you're right, this is not about constructor (I do not pass arguments there). Well, there's another reason - if I have 3 parameters, I'll have to do Resolve 3 times which is a bit of extra code. – queen3 Nov 06 '09 at 10:56
  • So, what you want is type-safe method injection? – R. Martinho Fernandes Nov 06 '09 at 10:58
  • No, it's a combination. All of them at once - type-safe, parameters, automatic pick-up... It's easy to check one feature, but a combination of them is not easy to verify unless one's familiar with particular container and knows its features. Thus the question. – queen3 Nov 06 '09 at 11:04

1 Answers1

30

I think you would be better off by defining an Abstract Factory that can create your class.

public interface IFactory
{
    MyClass Create(string data);
}

You could then create an implementation of IFactory like this:

public class MyFactory : IFactory
{
    private IDependency dependency;

    public MyFactory(IDependency dependency)
    {
        if (dependency == null)
        {
            throw new ArgumentNullException("dependency");
        }

        this.dependency = dependency;
    }

    #region IFactory Members

    public MyClass Create(string data)
    {
        return new MyClass(this.dependency, data);
    }

    #endregion
}

In your container, you would register both MyFactory and the implementation of IDependency.

You can now use the container to resolve the Factory, and the Factory to get the class:

var mc = container.Resolve<IFactory>().Create(data);

This approach is completely type-safe and nicely decouples the dependencies from run-time application data.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 7
    Bonus points for calling it by the name Abstract Factory, but using an interface. I've seen a lot of people that when they hear *Abstract* Factory go straight to abstract base class. – R. Martinho Fernandes Nov 06 '09 at 11:05
  • 2
    Yes I know this, though in my case I hesitate because for simple cases it's overkill to duplicate constructor with interface, factory, factory's constructor, Create method, etc. While good for complex cases, it's too "DI explosion" for simple things I think. And the question remains about automatic registration DI container. – queen3 Nov 06 '09 at 11:16
  • @queen3: I understand what you mean, but I'm not aware of any DI Containers that can do what you ask. Incidentally, AutoFixture (http://autofixture.codeplex.com/) has a syntax that is very close to the one you ask about, but it's not a DI Container :/ I did see your other question about auto-registration, but don't (yet) have a good answer. – Mark Seemann Nov 06 '09 at 11:41
  • I currently think about implementing both type-safe creation and auto-registration myself, for this I need to know "if type can be instantiated by container" from the type - I think http://stackoverflow.com/questions/1550190/list-all-types-registered-with-a-castle-windsor-container-instance will do but I'm not sure. Of course a container that does this already would be much better than a half-baked custom solution... – queen3 Nov 06 '09 at 11:47
  • @Martinho that point is from me because I thought immediately of an abstract base class... – msfanboy Jun 24 '11 at 21:54
  • @MarkSeemann How do I reuse a dependency created through a factory? Let's say I create `var mc` in the example above at one place in the code, but need exactly this instance at another place as well? I still don't have `var mc` in my container. The only way I could imagine is that `create(string data)` also stores the return object as a dependency in the factory, so that I can use the factory as a dependency somewhere else and use something like `Factory#get`, whenever I want to use the created `MyClass`? But this seems pretty awkward... – cobby Sep 25 '18 at 22:37
  • @cobby That sounds like a lifetime management issue. You'd basically need to give the object created Singleton lifetime, but associate each of such created objects with the data with which it was created. This could be a potential memory leak. What problem are you really trying to solve? You may want to ask a new question here on Stack Overflow... – Mark Seemann Sep 26 '18 at 06:12