ServiceContainer.AutoCreate question

6 views
Skip to first unread message

Zihotki

unread,
Feb 4, 2009, 6:15:01 PM2/4/09
to LinFu.Framework
Hello all,
I'm using ServiceContainer.AutoCreate to initialize some types
(exactly controllers in ASP.NET MVC application). It can be a lot of
controllers here so I decide to use auto creation. All is working fine
except one thing. I have few dependencies in controller and they are
not initialized by LinFu. Code of controller:
public class HomeController : Controller
{
[Inject]
public ICategoryRepository CategoryRepository { get; set; }


public ActionResult Index()
{
return View();
}
}

I initialize it using: container.AutoCreate(typeOfController)

ICategoryRepository is situated in other library and has an
implementation:
[Implements(typeof(ICategoryRepository),
LifecycleType.OncePerRequest)]
public class CategoryRepository : BaseRepository<Category>,
ICategoryRepository
{}

And I load this assembly into container using:
container.LoadFrom(typeof(BaseRepository<>).Assembly);

All looks fine but HomeController.CategoryRepository is always null.
I've debugged the AutoCreate code and it seems to me that LinFu
doesn't support such functionality, am I right?

Philip_L

unread,
Feb 4, 2009, 9:21:06 PM2/4/09
to LinFu.Framework
Hi Zihotki,

The reason why you're not getting property injection is that property
injection is only done if you use the GetService<T> method to
instantiate your controller type. Try this:

container.AddService<Controller>("HomeController",typeof
(HomeController));

// ICategoryRepository Property Injection will work here
var controller = container.GetService<Controller>("HomeController");


The AutoCreate method can be a little bit confusing since it was
originally designed to instantiate unregistered types without forcing
you to register it in the container. For the most part, you can think
of it as a low-level method that allows you to do constructor
injection on any type, but the caveat is that it doesn't do any other
form of injection.

In short, the answer to your question is just to register the type in
the container and use the GetService<T>() method for all the injection
goodness. :) Try it out, and let me know if it helps.

Regards,

Philip Laureano

Vasili Puchko

unread,
Feb 5, 2009, 1:09:21 PM2/5/09
to linfufr...@googlegroups.com
Philip,

Thank you for response. But for ASP.NET MVC architecture this way is not suitable. I'm developing a controller factory to create controllers. In order to create it I need to inherit from DefaultControllerFactory and override one method - public override IController CreateController(RequestContext requestContext, string controllerName) . At the moment my controller factory has the following code:

    public class LinFuControllerFactory : DefaultControllerFactory
    {
        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
            var type = GetControllerType(controllerName);

            if (type == null)
            {
                throw new InvalidOperationException(string.Format("Could not find a controller with the name {0}", controllerName));
            }

            return GetContainer(requestContext).AutoCreate(type) as IController;
        }

        protected virtual IServiceContainer GetContainer(RequestContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            var application = context.HttpContext.ApplicationInstance as LinFuHttpApplication;
            if (application == null)
            {
                throw new InvalidOperationException("You must from LinFuHttpApplication in your web project.");
            }

            var container = application.CurrentServiceContainer;
            if (container == null)
            {
                throw new InvalidOperationException("The container seems to be unavailable in your HttpApplication subclass");
            }

            return container;
        }

"GetControllerType" - is from base class, it returns a resolved type of a controller. And after we need to create an instance of this type. In this method I can't use constructions like 'var controller = container.GetService<Controller>("HomeController")' because in order to use this we need to register each controller in the container with unique name. I think that it's very inconveniently to register each controller with his name because this registration will be broken after we rename a controller. And this may cause a lot problems. It's more useful to use AutoCreate and not to add controllers to the IoC container because in this way all will work "from the box".
Another way to implement this is to create some autoregistration module/plugin. For example Ninject has a AutoControllerModule. It automatically registers all controllers in the assembly (usage sample: new AutoControllerModule(typeof(AdminController).Assembly) ). But at the moment I didn't know how to realize such functionality with LinFu. There are too few samples of writing plugins :( but I'm planning to look at this (and at all code of LinFu because it's very cool :) more closely. It will be very helpful if you give me some tips how to realize such functionality. I think that such module/plugin/container extension will be very helpful when working with a library of similar classes, like repositories. We'll not need to add ImplementsAttribute to each class and the code will be more clean. What do you think about this?

Best regards,
Vasili.

Philip Laureano

unread,
Feb 5, 2009, 6:19:34 PM2/5/09
to linfufr...@googlegroups.com

Hi Vasili,

Actually, you don't need to register each controller type in order to create it with the container. Here's how you can setup the container to dynamically create IController instances at runtime, based on the given controller name:

// Container setup code
Func<IFactoryRequest, IController> factoryMethod = request=>
{
        var container = request.Container;
        var controllerName = (string)request.Arguments[0];
       
        // Note: This should work as long as you can acess the GetControllerType() method from the functor itself
        var type = GetControllerType(controllerName);
       return container.AutoCreate(type) as IController;
}

// Register the factory method to create IController instances
container.AddService<IController>(factoryMethod, LifecycleType.OncePerRequest);

...and here's the modified ControllerFactory class:


    public class LinFuControllerFactory : DefaultControllerFactory
    {
        public override IController CreateController(
RequestContext requestContext, string controllerName)
        {
            var type = GetControllerType(controllerName);

            if (type == null)
            {
                throw new InvalidOperationException(string.Format("Could not find a controller with the name {0}", controllerName));
            }

            return container.GetService<IController>(controllerName);

        }

        protected virtual IServiceContainer GetContainer(RequestContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            var application = context.HttpContext.ApplicationInstance as LinFuHttpApplication;
            if (application == null)
            {
                throw new InvalidOperationException("You must from LinFuHttpApplication in your web project.");
            }

            var container = application.CurrentServiceContainer;
            if (container == null)
            {
                throw new InvalidOperationException("The container seems to be unavailable in your HttpApplication subclass");
            }

            return container;
        }

At first, it might look the same as the old code, but what the factory method is actually doing is resolving your controller type at runtime and using AutoCreate to do the constructor injection. The difference is that every controller that is created out of that factory method will have property injection enabled, and that one call to AddService() effectively registers all your controller types within any given assembly, provided that you can resolve the controller type using the GetControllerType() method.

Hopefully, that should fix your issues with registration and property injection. Right now, I'm working on a patch that will let you do property injection on all types without having to register it in the container, and I'll let you know as soon as it's available. Anyway, try this out, and let me know if there's anything else I can do to help you.

Regards,

Philip Laureano

Philip Laureano

unread,
Feb 5, 2009, 10:43:44 PM2/5/09
to linfufr...@googlegroups.com
Hi Vasili,

The AutoCreate method has now been patched to support property, method, and field injection. Once you get the latest version of LinFu.IOC, you should be ready to go.

It's available in the trunk, under revision 351. Enjoy!

Regards,

Philip Laureano

On Fri, Feb 6, 2009 at 2:09 AM, Vasili Puchko <zih...@gmail.com> wrote:

Vasili Puchko

unread,
Feb 8, 2009, 5:02:45 PM2/8/09
to linfufr...@googlegroups.com
Hi Philip,

Thanks a lot! I've started to write patch too but you was more faster
:). And by the word, I like the code of LinFu, it's very good and
interesting.

Kind Regards,
Vasili.

Reply all
Reply to author
Forward
0 new messages