Injecting dependencies into Tasks

Jun 1, 2008 at 2:38 PM
Is there a recommended way to inject dependencies (e.g. a repository object) into a Task?

Am loving the library so far. Was able to rip out all my custom workflow code in a matter of a couple of hours.
Coordinator
Jun 1, 2008 at 10:17 PM

I just checked in a version that will allow you to specify an IoC container when creating your StateMachine by wrapping your container in a very simple IObjectBuilder interface and passing the wrapper to the constructor of StateMachine. Then, this builder will be used to construct ITask objects and should inject anything you have configure.

public class WindosorContainerWrapper : IObjectBuilder { ... }

IObjectBuilder myWrapper = new WindsorContainerWrapper(containerInstance);
StateMachine newMachine = new StateMachine(myWrapper);

Let me know if this is what you were after.


kbaley wrote:
Is there a recommended way to inject dependencies (e.g. a repository object) into a Task?

Am loving the library so far. Was able to rip out all my custom workflow code in a matter of a couple of hours.



Jun 2, 2008 at 12:35 AM
I'll have to try it out to make sure but that sounds nigh on perfect. Thanks!
Jun 2, 2008 at 12:56 AM
Just downloaded the latest and it looks like it's missing some files. IObjectBuilder and DefaultObjectBuilder and one other that escapes me. Will try to get the latest directly from TFS tomorrow.


PlasticLIzard wrote:

I just checked in a version that will allow you to specify an IoC container when creating your StateMachine by wrapping your container in a very simple IObjectBuilder interface and passing the wrapper to the constructor of StateMachine. Then, this builder will be used to construct ITask objects and should inject anything you have configure.

public class WindosorContainerWrapper : IObjectBuilder { ... }

IObjectBuilder myWrapper = new WindsorContainerWrapper(containerInstance);
StateMachine newMachine = new StateMachine(myWrapper);

Let me know if this is what you were after.


kbaley wrote:
Is there a recommended way to inject dependencies (e.g. a repository object) into a Task?

Am loving the library so far. Was able to rip out all my custom workflow code in a matter of a couple of hours.






Coordinator
Jun 2, 2008 at 3:14 AM

Oops, not sure why those didn't make it. They should be checked in now, so either the download or the trunk should do the trick. Sorry about that :)

kbaley wrote:
Just downloaded the latest and it looks like it's missing some files. IObjectBuilder and DefaultObjectBuilder and one other that escapes me. Will try to get the latest directly from TFS tomorrow.


PlasticLIzard wrote:

I just checked in a version that will allow you to specify an IoC container when creating your StateMachine by wrapping your container in a very simple IObjectBuilder interface and passing the wrapper to the constructor of StateMachine. Then, this builder will be used to construct ITask objects and should inject anything you have configure.

public class WindosorContainerWrapper : IObjectBuilder { ... }

IObjectBuilder myWrapper = new WindsorContainerWrapper(containerInstance);
StateMachine newMachine = new StateMachine(myWrapper);

Let me know if this is what you were after.


kbaley wrote:
Is there a recommended way to inject dependencies (e.g. a repository object) into a Task?

Am loving the library so far. Was able to rip out all my custom workflow code in a matter of a couple of hours.









Jun 3, 2008 at 3:14 AM
Hey, Nathan.

Thanks for this. I'm playing with it now but there may be a bug in the TaskExecutionService. The constructor starts with the following code:

            if (objectBuilder == null)
                ObjectBuilder = new DefaultObjectBuilder();

            _factory = new TaskFactory(ObjectBuilder,taskAssemblies);

This will never initialize the TaskFactory with the objectBuilder you pass in. It will either initialize the factory with null (if you pass in a null builder) or with the DefaultObjectBuilder (if you pass in anything else).

I also have a question that is more architectural. I'm using this in an ASP.NET MVC app, so the container is created in the Global.asax.cs file. But I'm working with the workflow code at a lower level so I don't have direct access to the container. Any suggestions? The way I have it working now, I have to add an instance of WindsorContainer itself to my container.


PlasticLIzard wrote:

Oops, not sure why those didn't make it. They should be checked in now, so either the download or the trunk should do the trick. Sorry about that :)

kbaley wrote:
Just downloaded the latest and it looks like it's missing some files. IObjectBuilder and DefaultObjectBuilder and one other that escapes me. Will try to get the latest directly from TFS tomorrow.


PlasticLIzard wrote:

I just checked in a version that will allow you to specify an IoC container when creating your StateMachine by wrapping your container in a very simple IObjectBuilder interface and passing the wrapper to the constructor of StateMachine. Then, this builder will be used to construct ITask objects and should inject anything you have configure.

public class WindosorContainerWrapper : IObjectBuilder { ... }

IObjectBuilder myWrapper = new WindsorContainerWrapper(containerInstance);
StateMachine newMachine = new StateMachine(myWrapper);

Let me know if this is what you were after.


kbaley wrote:
Is there a recommended way to inject dependencies (e.g. a repository object) into a Task?

Am loving the library so far. Was able to rip out all my custom workflow code in a matter of a couple of hours.












Coordinator
Jun 3, 2008 at 5:37 AM
Yep, that was a bug, it is fixed now. As for the MVC question, I'm not sure i'm following you. I haven't spent much time with MS MVC yet, so I am unfamiliar with how it integrates with IoC containers. However, if you don't have access to the IoC at all, I can't think of how the workflow library will get at it - if you can give me some more context i'd be happy to add any support for the scenario that i can. If you don't have access to the container, how are you creating your objects?

kbaley wrote:
Hey, Nathan.

Thanks for this. I'm playing with it now but there may be a bug in the TaskExecutionService. The constructor starts with the following code:

            if (objectBuilder == null)
                ObjectBuilder = new DefaultObjectBuilder();

            _factory = new TaskFactory(ObjectBuilder,taskAssemblies);

This will never initialize the TaskFactory with the objectBuilder you pass in. It will either initialize the factory with null (if you pass in a null builder) or with the DefaultObjectBuilder (if you pass in anything else).

I also have a question that is more architectural. I'm using this in an ASP.NET MVC app, so the container is created in the Global.asax.cs file. But I'm working with the workflow code at a lower level so I don't have direct access to the container. Any suggestions? The way I have it working now, I have to add an instance of WindsorContainer itself to my container.


PlasticLIzard wrote:

Oops, not sure why those didn't make it. They should be checked in now, so either the download or the trunk should do the trick. Sorry about that :)

kbaley wrote:
Just downloaded the latest and it looks like it's missing some files. IObjectBuilder and DefaultObjectBuilder and one other that escapes me. Will try to get the latest directly from TFS tomorrow.


PlasticLIzard wrote:

I just checked in a version that will allow you to specify an IoC container when creating your StateMachine by wrapping your container in a very simple IObjectBuilder interface and passing the wrapper to the constructor of StateMachine. Then, this builder will be used to construct ITask objects and should inject anything you have configure.

public class WindosorContainerWrapper : IObjectBuilder { ... }

IObjectBuilder myWrapper = new WindsorContainerWrapper(containerInstance);
StateMachine newMachine = new StateMachine(myWrapper);

Let me know if this is what you were after.


kbaley wrote:
Is there a recommended way to inject dependencies (e.g. a repository object) into a Task?

Am loving the library so far. Was able to rip out all my custom workflow code in a matter of a couple of hours.















Jun 3, 2008 at 1:23 PM
Nathan,

I've managed to work through my problem. I'm using MvcContrib with MVC which allows me to use Windsor to create my controllers and it will wire up all my dependencies automatically. I have no need to reference the container anywhere except in Global.asax.cs when I create the ControllerFactory.

So I created my own ObjectBuilder and injected it into my workflow service. This means the ControllerFactory is able to get my ObjectBuilder from the container when it creates the service. The problem I had was that I thought I would have to create an ObjectBuilder that took a WindsorContainer in its constructor. Thus, I would have to add an instance of the container itself into the container.

The solution was not to create a WindsorObjectBuilder but my own custom implementation:

    public class ObjectBuilder : IObjectBuilder
    {
        private readonly Dictionary<Type, object> _types = new Dictionary<Type, object>( );
        private readonly List<ITask> _tasks = new List<ITask>( );

        public ObjectBuilder( IPersistStateTask persistStateTask )
        {
            _tasks.Add( persistStateTask );
        }

        public T Create<T>( Type t ) where T : class
        {
            return (T)_tasks.Find( type => type.GetType( ) == t );
        }
    }

The IPersistState task itself is also registered with the container so this builder will get created with the task injected into it. Kind of similar to the DefaultObjectBuilder but since the task is registered with the container, so are its dependencies. Thus, I can have a PersistState task with a repository injected into it.

Not sure I explained that properly. The short answer is that it's working now. Only iffy thing is that I'll need to update the builder every time I add a new task. If you're interested, I'm happy to talk over the details over Skype. E-mail address is kyle@baley.org.

Kyle
Coordinator
Jun 3, 2008 at 6:28 PM

Kyle, i don't think there will be a problem with adding an instance of the container to the container. I have seen this done with Spring, and i'm sure it would work in Windsor as well. I would consider adding an instance of your wrapped container (implementing IObjectBuilder) and then using the container to create your StateMachine. The IoC should have no trouble injecting a singleton instance of itself into the constructor of StateMachine. Maybe I'm not fully understanding you, though, that is often the case. However, if you're happy with your solution I am happy. :)

kbaley wrote:
Nathan,

I've managed to work through my problem. I'm using MvcContrib with MVC which allows me to use Windsor to create my controllers and it will wire up all my dependencies automatically. I have no need to reference the container anywhere except in Global.asax.cs when I create the ControllerFactory.

So I created my own ObjectBuilder and injected it into my workflow service. This means the ControllerFactory is able to get my ObjectBuilder from the container when it creates the service. The problem I had was that I thought I would have to create an ObjectBuilder that took a WindsorContainer in its constructor. Thus, I would have to add an instance of the container itself into the container.

The solution was not to create a WindsorObjectBuilder but my own custom implementation:

    public class ObjectBuilder : IObjectBuilder
    {
        private readonly Dictionary<Type, object> _types = new Dictionary<Type, object>( );
        private readonly List<ITask> _tasks = new List<ITask>( );

        public ObjectBuilder( IPersistStateTask persistStateTask )
        {
            _tasks.Add( persistStateTask );
        }

        public T Create<T>( Type t ) where T : class
        {
            return (T)_tasks.Find( type => type.GetType( ) == t );
        }
    }

The IPersistState task itself is also registered with the container so this builder will get created with the task injected into it. Kind of similar to the DefaultObjectBuilder but since the task is registered with the container, so are its dependencies. Thus, I can have a PersistState task with a repository injected into it.

Not sure I explained that properly. The short answer is that it's working now. Only iffy thing is that I'll need to update the builder every time I add a new task. If you're interested, I'm happy to talk over the details over Skype. E-mail address is kyle@baley.org.

Kyle



Jun 3, 2008 at 10:14 PM
Yeah, I know it's possible. I did it last night when I spiked it out with your code changes. Just feels weird. But, yes, I'm happy with the alternate solution I have. For the time being.
Coordinator
Jun 3, 2008 at 10:40 PM
If you end up with something that feels more comfortable and straightforward let me know and I'll be happy to apply the change.

kbaley wrote:
Yeah, I know it's possible. I did it last night when I spiked it out with your code changes. Just feels weird. But, yes, I'm happy with the alternate solution I have. For the time being.


Feb 19, 2009 at 3:00 AM
i am trying to inject dependencies into Task. Here you have mentioned to pass IObjectBuilder to the constructor of StateMachine. But i see the constructor doesn't accept any parameters. Has the code changed since then or am I missing the obvious?

cheers
Coordinator
Feb 19, 2009 at 4:18 AM
Yes, it looks like something changed. There is a service locator now (although it doesn't seem to be getting much use in the library, might have been overkill). Anyway, if you have an implementation of IObjectBuilder you want to use to create your tasks, you can register it when your app inits like this:

ServiceLocator.RegisterService<IObjectBuilder>(myObjectBuilder);


Feb 19, 2009 at 9:33 AM
Any thoughts on adding support for CommonServiceLocator?
Coordinator
Feb 20, 2009 at 7:14 PM
Yes, I have thought about it a few times - I wasn't sure if anyone is actually using the CommonServiceLocator, so I haven't actually bothered to make the change thus far, as I hate to take a dependency on an external library without a good reason - are you actually using CommonServiceLocator + adapters, or are you asking out of curiosity?
Feb 24, 2009 at 11:52 PM
thanks a lot. it works as expected.

been busy with other stuff and couldn't try it out till today.




Mar 1, 2009 at 10:33 PM
We do use the CommonServiceLocator. The recent release of Prism 2.0 also uses it, as well as the Caliburn beta. But yes, still asking out of curiosity.
Mar 22, 2009 at 8:53 AM
Is there any example to do accomplish these tasks? Sorry, I'm lost with all the DI techniques being described here.
Mar 24, 2009 at 12:17 AM
hi nirataro,

first you need to define your implementation of IObjectBuilder. I have defined mine as below:
public class WindsorObjectBuilder : IObjectBuilder
    {
        private readonly IWindsorContainer _container;

        public WindsorObjectBuilder(IWindsorContainer container)
        {
            _container = container;
        }

        public T Create<T>() where T : class
        {
            return _container.Resolve<T>();
        }

        public T Create<T>(Type type) where T : class
        {          
            return _container.Resolve<T>();
        }
    }
then register it as suggested by plastic lizard:
ServiceLocator.RegisterService<IObjectBuilder>(new WindsorObjectBuilder(windsorContainer));