servicestack with funq - autowiring by convention
S

2

6

I have a service which takes an IMyDependency in its constructor. IMyDependency, MyDependency and the service all live in the same assembly. MyDependency has a single, public, parameterless constructor.

To my surprise, this did not work:

container.RegisterAutoWired<IMyDependency>();

It throws a "System.NullReferenceException".

It works if I do this:

container.RegisterAutoWiredAs<MyDependency, IMyDependency>();

But then, so does this:

container.RegisterAs<MyDependency, IMyDependency>();

So what is the difference? If 'auto wiring' cannot find a concrete implementation, and it makes no difference to whether services requiring the dependency can be resolved, then what is auto wiring?

Is Funq supposed to be able to find your concrete implementations by convention? If so, what is that convention, if not same-named-ness?

Thanks.

Solanaceous answered 26/4, 2013 at 0:19 Comment(0)
T
5

For simple queries like this it's best to just contact the source, e.g. here is the source code for RegisterAutoWired:

public IRegistration<T> RegisterAutoWired<T>()
{
    var serviceFactory = GenerateAutoWireFn<T>();
    return this.Register(serviceFactory);
}

It generates an auto-wired factory over a Concrete implementation. An interface has no implementation, it needs to be a concrete class.

And the source code for RegisterAs:

public IRegistration<TAs> RegisterAs<T, TAs>() where T : TAs 
{
    return this.RegisterAutoWiredAs<T, TAs>();
}

Which is just a shorter alias you can use instead of RegisterAutoWiredAs.

Tertia answered 26/4, 2013 at 1:3 Comment(3)
So, if I understand this correctly, I should be doing: container.RegisterAutoWired<MyDependency>(); However, if I do only this, at runtime (when I call the service with the dependency), I get "Required dependency of type IMyDependency could not be resolved". So I am still unclear on the purpose of this method, as the container does not seem able to find the interface corresponding to the concrete type registered.Solanaceous
It's pretty straight forward, it will simply inject what you Register, if you Register<MyDependency> than it will inject all MyDependency properties. If you want it to inject IMyDependency properties than you need to call RegisterAs<MyDependency,IMyDependency>.Tertia
In my project I have like ~200 classes by convention: MyClass : IMyClass. So according to this each time I implement new class or remove could do wiring for it? Unity, Windsor and StructureMap has autowire by convention.Aargau
J
8

Do you mean "how can I implement a solution to search through assemblies and automatically register classes in ServiceStack IOC based on a convention?"

If so, I might have a solution for you:

  1. Create an interface that your inject-able classes will implement.
  2. Have your inject-able classes implement that interface.
  3. In the boot-strapping code use reflection to search your assemblies and get a list of all of the classes that implement the inject-able interface.
  4. Use reflection to get the class name and interface based on your conventions.
  5. Call the ServiceStack IOC method RegisterAutoWiredType and pass in the class and interface to register them.

For example if our naming convention is ClassName IClassName:

private static void RegisterCustomTypes(Container container)
{
  //Get the Assembly Where the injectable classes are located.
  var assembly = Assembly.GetAssembly(typeof(IInjectable));

  //Get the injectable classes 
  var types =assembly.GetTypes()
    .Where(m => m.IsClass && m.GetInterface("IInjectable") != null);

  //loop through the injectable classes
  foreach (var theType in types)
  {
    //set up the naming convention
    var className = theType.Name;
    var interfaceName = string.Concat("I", className);
    //create the interface based on the naming convention
    var theInterface = theType.GetInterface(interfaceName);
    //register the type with the convention
    container.RegisterAutoWiredType(theType, theInterface);
  }
}

public interface IInjectable
{

}

//This class can be injected
public interface ITestManager : IInjectable
{
    void Execute(int id);
}

public class TestManager : ITestManager
{
    public void Execute(int id)
    {
        throw new System.NotImplementedException();
    }
}
Junkie answered 15/10, 2013 at 13:52 Comment(4)
Since my original post, I have understood that Func does not have built in assembly searching. If you implement your own reflection as suggested here, do you think any advantage (such as performance) would remain in using Func, over another solution such as Ninject, etc.?Solanaceous
Not sure. Maybe mythz has some guidance on that?Junkie
@Saber, DI only runs once at startup of the web app. Performance difference can be ignored there imo. Windsor or Ninject ...etc, they are all good. Only reason I use Funq over others is because it comes with SS, I am too lazy to maintain another IoC separated from SS package.Tello
@KenBurkhardt: What if i'm using Strategy, eg.? As in there are more than one implementations of an interface in the assembly?Paquin
T
5

For simple queries like this it's best to just contact the source, e.g. here is the source code for RegisterAutoWired:

public IRegistration<T> RegisterAutoWired<T>()
{
    var serviceFactory = GenerateAutoWireFn<T>();
    return this.Register(serviceFactory);
}

It generates an auto-wired factory over a Concrete implementation. An interface has no implementation, it needs to be a concrete class.

And the source code for RegisterAs:

public IRegistration<TAs> RegisterAs<T, TAs>() where T : TAs 
{
    return this.RegisterAutoWiredAs<T, TAs>();
}

Which is just a shorter alias you can use instead of RegisterAutoWiredAs.

Tertia answered 26/4, 2013 at 1:3 Comment(3)
So, if I understand this correctly, I should be doing: container.RegisterAutoWired<MyDependency>(); However, if I do only this, at runtime (when I call the service with the dependency), I get "Required dependency of type IMyDependency could not be resolved". So I am still unclear on the purpose of this method, as the container does not seem able to find the interface corresponding to the concrete type registered.Solanaceous
It's pretty straight forward, it will simply inject what you Register, if you Register<MyDependency> than it will inject all MyDependency properties. If you want it to inject IMyDependency properties than you need to call RegisterAs<MyDependency,IMyDependency>.Tertia
In my project I have like ~200 classes by convention: MyClass : IMyClass. So according to this each time I implement new class or remove could do wiring for it? Unity, Windsor and StructureMap has autowire by convention.Aargau

© 2022 - 2024 — McMap. All rights reserved.