Dependency injection without writing an autofac module

Topics: Core, General, Troubleshooting, Writing modules
Dec 10, 2014 at 5:43 PM
Hello,
I’m new to Orchard and trying to understand how my own module should be composed according to Orchard rules.
I was asked to create service class that inherits from Orchard.Component and use it in controller constructor, but without writing an autofac module which registers the dependencies.

TestService.cs:
namespace MyModule.Test.Services
{
    public class TestService : Component
    {
        public string TestMethod()
        {
            return "Hello !!!";
        }
    }
} 
HomeController.cs
namespace MyModule.Test.Controllers
{
    [Themed]
    public class HomeController : Controller {
        private TestService _testService;

        public HomeController(TestService testService)
        {
            _testService = testService;
        }

        // GET: Home
        public ActionResult Index()
        {
            return View((object)_testService.TestMethod());
        }
    }
}
I taught it should work but got an error:

Server Error in '/' Application.

None of the constructors found with 'Orchard.Environment.AutofacUtil.DynamicProxy2.ConstructorFinderWrapper' on type 'MyModule.Test.Controllers.HomeController' can be invoked with the available services and parameters:
Cannot resolve parameter 'MyModule.Test.Services.TestService testService' of constructor 'Void .ctor(MyModule.Test.Services.TestService)'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: Autofac.Core.DependencyResolutionException: None of the constructors found with 'Orchard.Environment.AutofacUtil.DynamicProxy2.ConstructorFinderWrapper' on type 'MyModule.Test.Controllers.HomeController' can be invoked with the available services and parameters:
Cannot resolve parameter 'MyModule.Test.Services.TestService testService' of constructor 'Void .ctor(MyModule.Test.Services.TestService)'.

Only way I found to solve this problem was to add to TestService.cs ITestService interface and use it in HomeController.cs:

TestService.cs:
namespace MyModule.Test.Services
{
    public interface ITestService : IDependency
   {
        string TestMethod();
    }
    …
} 
HomeController.cs
namespace MyModule.Test.Controllers
{
    [Themed]
    public class HomeController : Controller {
        private ITestService _testService;

        …
    }
}
But I wonder should I still define an interface even if I use it only once and is there any other possibility to stay away of using interface in such simple situation, when it seems more like just duplicating the code?
I’m sure that someone has already asked such question, but I failed to find it.
Developer
Dec 10, 2014 at 5:47 PM
Edited Dec 10, 2014 at 5:48 PM
You wouldnt - you need to do this.
public interface ITestService : IDependency {
    string TestMethod()
}

public class TestService : Component, ITestService
{
    public string TestMethod()
    {
        return "Hello !!!";
    }
}

namespace MyModule.Test.Controllers
{
    [Themed]
    public class HomeController : Controller {
        private ITestService _testService;

        public HomeController(ITestService testService)
        {
            _testService = testService;
        }

        // GET: Home
        public ActionResult Index()
        {
            return View((object)_testService.TestMethod());
        }
    }
}
If you read what a Component class gives you, its just localization and logging, nothing more.
Dec 17, 2014 at 11:13 AM
I’m sorry, I was not clear enough. I mean, that I have already done what You advised me and it works fine. But I wonder is there any other way to do It, avoiding creation of interfaces. I thought Component already inherits from IDependency and my class should be picked up by Autofac, but I was wrong and now I'm trying to understand is there any other way to do it and, if no, why exactly it works only in way, You showed.
Developer
Dec 17, 2014 at 9:55 PM
Edited Dec 17, 2014 at 9:55 PM
As you pointed out yourself, there are two ways to register a component: either by convention (implementing an interface that derives from IDependency) or by writing an Autofac module (which could implement your own little convention if you like). There's no other way that I can think of - Autofac can't magically know what components to register. Either you register it or have it discovered via some convention (which you need to configure through an Autofac module if you don't like Orchard's IDependency convention).
Dec 30, 2014 at 9:36 PM
Yarrem - may I also add that an important principal of software design and architecture is "program against interfaces, not implementations". It's one of the GoF "SOLID" principles. Lots of good things happen when you follow best practices, and this is definitely one of them. The "cost" of introducing a simple (Replaceable! Testable!) interface in this case is swamped by the benefits. Do it, and feel good about it!

My $0.02.

Kurt
Marked as answer by yarrem on 1/15/2015 at 1:57 AM
Jan 15, 2015 at 9:58 AM
Sorry, for so late responce! I saw the idea, thank You all for Your replys!
Apr 30, 2015 at 10:19 AM
Hi,

I have found that IDepandency is only working when the interface inherits it.
When I incidently made an implementor class inheriting IDependency, and the interface not, service object resolution has failed.
That can be a problem for example where someone wants to implement an 3rd party or predefined interface outside orchard.
It can be possible the discovering process to seek IDependecy on classes too?
That would allow more flexible setup, eg you can have classes with automatic registration and without automatic registration of the same interface (let suppose some classes need special registration setup in a module, and some others are happy with automatic registration), not to mention that external interface have not to be redeclared.
May 11, 2015 at 10:48 PM
I don't get why you couldn't implement both. Let's say your class needs to implement IExternalApi and also needs to use dependency injection. You just need to declare your interface
public interface IMyService : IExternalApi, IDependency
{
}
and make your class implement IMyService.

Did I miss the point of your remark?