152

I am trying to get Unity to manage the creation of my objects and I want to have some initialization parameters that are not known until run-time:

At the moment the only way I could think of the way to do it is to have an Init method on the interface.

interface IMyIntf {
  void Initialize(string runTimeParam);
  string RunTimeParam { get; }
}

Then to use it (in Unity) I would do this:

var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");

In this scenario runTimeParam param is determined at run-time based on user input. The trivial case here simply returns the value of runTimeParam but in reality the parameter will be something like file name and initialize method will do something with the file.

This creates a number of issues, namely that the Initialize method is available on the interface and can be called multiple times. Setting a flag in the implementation and throwing exception on repeated call to Initialize seems way clunky.

At the point where I resolve my interface I don't want to know anything about the implementation of IMyIntf. What I do want, though, is the knowledge that this interface needs certain one time initialization parameters. Is there a way to somehow annotate(attributes?) the interface with this information and pass those to framework when the object is created?

Edit: Described the interface a bit more.

Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
Igor Zevaka
  • 74,528
  • 26
  • 112
  • 128
  • 9
    You're missing the point of using a DI container. Dependencies are supposed to be resolved for you. – Pierreten Dec 22 '09 at 00:50
  • Where are you getting your needed parameters from? (config file, db, ??) – Jaime Dec 22 '09 at 00:58
  • `runTimeParam` is a dependency that is determined at run-time based on a user input. Should the alternative for this be splitting it into two interfaces - one for initialization and another for storing values? – Igor Zevaka Dec 22 '09 at 01:27
  • dependency in IoC, usually refers to dependency to other ref type classes or objects which can be determined at IoC initialization phase. If your class needs just some values to work, that's where the Initialize() method in your class becomes handy. – The Light Oct 04 '11 at 13:25
  • I mean imagine there are 100 classes in your app on which this approach can be applied; then you'll have to create extra 100 factory classes+100 interfaces for your classes and you could get away with if you were just using the Initialize() method. – The Light Oct 04 '11 at 15:40

5 Answers5

286

Any place where you need a run-time value to construct a particular dependency, Abstract Factory is the solution.

Having Initialize methods on the interfaces smells of a Leaky Abstraction.

In your case I would say that you should model the IMyIntf interface on how you need to use it - not how you intent to create implementations thereof. That's an implementation detail.

Thus, the interface should simply be:

public interface IMyIntf
{
    string RunTimeParam { get; }
}

Now define the Abstract Factory:

public interface IMyIntfFactory
{
    IMyIntf Create(string runTimeParam);
}

You can now create a concrete implementation of IMyIntfFactory that creates concrete instances of IMyIntf like this one:

public class MyIntf : IMyIntf
{
    private readonly string runTimeParam;

    public MyIntf(string runTimeParam)
    {
        if(runTimeParam == null)
        {
            throw new ArgumentNullException("runTimeParam");
        }

        this.runTimeParam = runTimeParam;
    }

    public string RunTimeParam
    {
        get { return this.runTimeParam; }
    }
}

Notice how this allows us to protect the class' invariants by use of the readonly keyword. No smelly Initialize methods are necessary.

An IMyIntfFactory implementation may be as simple as this:

public class MyIntfFactory : IMyIntfFactory
{
    public IMyIntf Create(string runTimeParam)
    {
        return new MyIntf(runTimeParam);
    }
}

In all your consumers where you need an IMyIntf instance, you simply take a dependency on IMyIntfFactory by requesting it through Constructor Injection.

Any DI Container worth its salt will be able to auto-wire an IMyIntfFactory instance for you if you register it correctly.

Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    This is great, I think it solves most of my concerns, except for the Factory interface having a dependency on `MyIntf` class. Am I just trying to get my DI container to do too much? I guess there is only one place where there is a dependency on the interface implementation, so if I want to swap it out for another implementation I just write another factory. Don't worry about answering that, I already answered it for myself :)) – Igor Zevaka Dec 22 '09 at 20:52
  • 1
    I understand this approach reduces the need to call the Initialize() method for the MyIntF class. I also see the point that your DI container doesn't have to register the MyIntF type but only the MyIntfFactory type. since using an abstract factory adds a new layer of complexity to the app, I'm not 100% convinced this has advantage over simply using the Initialize() method? – The Light Oct 04 '11 at 15:24
  • 13
    The problem is that a method (like Initialize) is part of your API, whereas the constructor is not. http://blog.ploeh.dk/2011/02/28/InterfacesAreAccessModifiers.aspx – Mark Seemann Oct 05 '11 at 09:10
  • 13
    Furthermore, an Initialize method indicates Temporal Coupling: http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling.aspx – Mark Seemann Oct 05 '11 at 09:11
  • @Mark. This is fine for 1 or 2 initialisation properties but what if the class required different construction (previously supported by virtue of different constructors), however I'd hazard a guess taht we start heading down the violation of SRP here or some other design 'guideline'.... – brumScouse Jul 26 '12 at 14:37
  • @brumScouse I'm not sure I completely understand the question, but does this help? http://stackoverflow.com/questions/2420193/dependency-injection-constructor-madness/2420245#2420245 – Mark Seemann Jul 26 '12 at 20:56
  • 1
    @Mark Thanks for this excellent example. How would you recommend handling the case of conditional construction/instantiation? Our case is that we have we have a class that requires a runTimeParam as you demonstrated above, but the runTimeParam might not be initially set. We would want to Create our class once this runTimeParam has a valid value. Would you use eventing/messaging to notify when this object can be constructed? More specifically our Workspace class cannot be constructed until the user configures valid database connection info. – Darlene Aug 01 '13 at 16:51
  • 2
    @Darlene You might be able to use a lazily initialized Decorator, as described in section 8.3.6 of [my book](http://bit.ly/b5Vir7). I also provide an example of something similar in my presentation [Big Object Graphs Up Front](https://vimeo.com/68378923). – Mark Seemann Aug 01 '13 at 17:52
  • 2
    @Mark If the factory's creation of the `MyIntf` implementation requires more than `runTimeParam` (read: other services that one would want resolved by an IoC), then you're still faced with resolving those dependencies in your factory. I like the @PhilSandler answer of passing those dependencies into the *factory's* constructor to solve this - is that your take on it as well? – Jeff Aug 05 '13 at 17:10
  • @Lumirris Yes, does this help? http://blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory – Mark Seemann Aug 05 '13 at 18:57
  • @Mark Yes, that helps but your linked article doesn't seem to address the notion of the factory's output's *known dependencies* which can be wired up with the IoC vs runtime parameter values that influence the choice of implementation returned by the factory. Particularly when you want a default implementation when the runtime parameter value is not handled, it seems like some of the conditional binding IoC extensions give you enough rope to hang yourself and use the IoC as an abstract factory, a [bad|good] thing depending on who you listen to. – Jeff Aug 05 '13 at 21:45
  • @Lumirris Perhaps this article series will be helpful to you, then... http://blog.ploeh.dk/2013/01/07/RoleHints – Mark Seemann Aug 06 '13 at 05:08
  • 2
    Also great stuff, but [your answer](http://stackoverflow.com/a/1686887/533958) to this other question really got to my point. – Jeff Aug 08 '13 at 16:34
  • @MarkSeemann "Any place where you need a run-time value to construct a particular dependency, Abstract Factory is the solution." What if you need a run-time value to finish constructing a particular dependency in the container? Would you simply call a setter on the factory to "finish the set up" and use the factory anywhere else with a parameter-free `create()` method? – cobby Sep 26 '18 at 00:16
  • @cobby That doesn't sound like a design I would be likely to employ. Why would you want to do something like this? I'm not sure I understand what you're trying to accomplish, so I'm wondering if this is an [XY problem](https://meta.stackexchange.com/q/66377/135815). – Mark Seemann Sep 26 '18 at 06:14
  • @MarkSeemann https://stackoverflow.com/questions/52507754/is-it-a-bad-practice-to-set-dependencies-to-null-in-a-ioc-container-and-supply-t refers to your post and exactly describes the problem I'm pointing to. What's your take on that? – cobby Sep 26 '18 at 15:48
  • Seems overengineered for me. Why not just use constructor directly to instantiate MyIntf? – Loreno Feb 06 '19 at 11:09
  • @Loreno Constructors aren't part of interfaces (in C# at least), so if you want to program to interfaces, there's no constructor to call. – Mark Seemann Feb 06 '19 at 15:59
  • @MarkSeemann Ok Mark, but, in your opinion, should we completely abandon using constructors to instantiate objects (outside of factories)? I'm curious what is your approach. Let's say you have a very simple service, where creating a factory would double amount of code actually. Would you create that factory anyway? – Loreno Feb 07 '19 at 07:33
  • @Loreno This question is explicitly about how to initialise objects with run-time parameters, while using Dependency Injection. If you have no run-time parameters, you can initialise objects normally in the application's [Composition Root](http://blog.ploeh.dk/2011/07/28/CompositionRoot); in that case, no factory is required. – Mark Seemann Feb 07 '19 at 08:11
  • @MarkSeemann Yeah, you're right. However, I don't know about Unity, but AutoFac lets you use constructors directly with ContainerBuilder's Register method. – Loreno Feb 07 '19 at 09:23
16

Usually when you encounter this situation, you need to revisit your design and determine if you are mixing your stateful/data objects with your pure services. In most (not all) cases, you will want to keep these two types of objects separate.

If you do need a context-specific parameter passed in the constructor, one option is to create a factory that resolves your service dependencies via the constructor, and takes your run-time parameter as a parameter of the Create() method (or Generate(), Build() or whatever you name your factory methods).

Having setters or an Initialize() method are generally thought to be bad design, as you need to "remember" to call them and make sure they don't open up too much of your implementation's state (i.e. what is to stop someone from re-calling initialize or the setter?).

Phil Sandler
  • 27,544
  • 21
  • 86
  • 147
5

I also have come across this situation a few times in environments where I am dynamically creating ViewModel objects based on Model objects (outlined really well by this other Stackoverflow post).

I liked how the Ninject extension which allows you to dynamically create factories based on interfaces:

Bind<IMyFactory>().ToFactory();

I could not find any similar functionality directly in Unity; so I wrote my own extension to the IUnityContainer which allows you to register factories that will create new objects based on data from existing objects essentially mapping from one type hierarchy to a different type hierarchy: UnityMappingFactory@GitHub

With a goal of simplicity and readability, I ended up with an extension that allows you to directly specify the mappings without declaring individual factory classes or interfaces (a real time saver). You just add the mappings right where you register the classes during the normal bootstrapping process...

//make sure to register the output...
container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>();
container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>();

//define the mapping between different class hierarchies...
container.RegisterFactory<IWidget, IWidgetViewModel>()
.AddMap<IImageWidget, IImageWidgetViewModel>()
.AddMap<ITextWidget, ITextWidgetViewModel>();

Then you just declare the mapping factory interface in the constructor for CI and use its Create() method...

public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { }

public TextWidgetViewModel(ITextWidget widget) { }

public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory)
{
    IList<IWidgetViewModel> children = new List<IWidgetViewModel>();
    foreach (IWidget w in data.Widgets)
        children.Add(factory.Create(w));
}

As an added bonus, any additional dependencies in the constructor of the mapped classes will also get resolved during object creation.

Obviously, this won't solve every problem but it has served me pretty well so far so I thought I should share it. There is more documentation on the project's site on GitHub.

Community
  • 1
  • 1
jigamiller
  • 513
  • 6
  • 7
1

I can't answer with specific Unity terminology but it sounds like you are just learning about dependency injection. If so, I urge you to read the brief, clear, and information packed user guide for Ninject.

This will walk you through the various option you have when using DI, and how to account for the specific issues that you'll face along the way. In your case, you would most likely want to use the DI container to instantiate your objects, and have that object get a reference to each of its dependencies through the constructor.

The walkthrough also details how to annotate methods, properties, and even parameters using attributes to distinguish them at runtime.

Even if you do not use Ninject, the walkthrough will give you the concepts and terminology of the functionality that suits your purpose, and you should be able to map that knowledge to Unity or other DI frameworks (or convince yout to give Ninject a try).

anthony
  • 40,424
  • 5
  • 55
  • 128
  • THanks for that. I am actually evaluating DI frameworks and NInject was going to be my next one. – Igor Zevaka Dec 22 '09 at 00:59
  • @johann: providers? https://github.com/ninject/ninject/wiki/Providers%2C-Factory-Methods-and-the-Activation-Context – anthony Feb 21 '11 at 05:00
1

I think I solved it and it feels rather wholesome, so it must be half right :))

I split IMyIntf into a "getter" and a "setter" interfaces. So:

interface IMyIntf {
  string RunTimeParam { get; }
}


interface IMyIntfSetter {
  void Initialize(string runTimeParam);
  IMyIntf MyIntf {get; }
}

Then the implementation:

class MyIntfImpl : IMyIntf, IMyIntfSetter {
  string _runTimeParam;

  void Initialize(string runTimeParam) {
    _runTimeParam = runTimeParam;
  }

  string RunTimeParam { get; }

  IMyIntf MyIntf {get {return this;} }
}

//Unity configuration:
//Only the setter is mapped to the implementation.
container.RegisterType<IMyIntfSetter, MyIntfImpl>();
//To retrieve an instance of IMyIntf:
//1. create the setter
IMyIntfSetter setter = container.Resolve<IMyIntfSetter>();
//2. Init it
setter.Initialize("someparam");
//3. Use the IMyIntf accessor
IMyIntf intf = setter.MyIntf;

IMyIntfSetter.Initialize() can still be called multiple times but using bits of Service Locator paradigm we can wrap it up quite nicely so that IMyIntfSetter is almost an internal interface that is distinct from IMyIntf.

Igor Zevaka
  • 74,528
  • 26
  • 112
  • 128
  • 14
    This isn't a particularly good solution since it relies on an Initialize method, which is a Leaky Abstraction. Btw, this doesn't look like Service Locator, but more like Interface Injection. In any case, see my answer for a better solution. – Mark Seemann Dec 22 '09 at 08:52