简体   繁体   中英

Dependency Injection - How to inject implementation of interface using Simple Injector when the implementation uses Unity for DI

I have two separate projects... One project is using Simple Injector and the other Unity. From the simple injector project, I am attempting to register the interface/implementation of a class located within the project using Unity for its DI. I can successfully do this and gain access to the class but anything marked as a Unity [Dependency] within that class does not resolve. I can register those dependencies to the Simple Injector container but it loses it once crossing into the class using Unity.

Example:

Project1 (using Simple Injector)

public class StartUp {
    var container = new Container();
    container.RegisterSingleton(typeof(IGenericRepo<>), typeof(GenericRepo));
    container.RegisterSingleton<IService, Service>();
    //more code below etc...
}

public class TestController
{
    private readonly IService service;

    public TestController(IService service)
    {
        this.service = service;
    }

    public void TestMethod()
    {
        var test = service.GetEverything();
    }
}

Project2 (using Unity)

public class Service : IService
{
    [Dependency]
    public IGenericRepo<ServiceObj> _serviceRepo { private get; set; }

    public IQueryable<ServiceObj> GetEverything()
    {
        return _serviceRepo.Get();
    }
}

With the example above, I can get to the GetEverything method within Project 2, but then the _serviceRepo dependecy is null. Is there a way for it to know to use the registered GenericRepo<> from Project1?

Is this possible to do?

Thank you! Ryan

If I understand correctly Project 2 is some shared project which is used in other projects which use Unity for Dependency Injection. Unity uses property injection in this case by annoting the property with the DependencyAttribute .

Simple Injector is capable of doing property injection. Property injection however should only be used for optional dependencies. And as you're running into a NullReferenceException this dependency is not optional!

So you should move the IGenericRepo<ServiceObj> to the constructor of IService . This makes it clear to users of IService that it needs a repository to function correctly. This becomes especially useful when you would unittest IService as the constructor will in this case clearly communicates that it needs a repository.

By moving the dependency to the constructor Simple Injector will inject the repository correctly or throw an ActivationException if the configuration is not valid.

You can test, and I recommend doing this, by calling container.Verify() as you can read here: Verify the container's configuration . This will make the application to fail fast .

If project 2 can't be refactored or for some other compelling reason constructor injection is not an option, Simple Injector does support property injection. It does however not support this out-of-the-box. One of the design principles of Simple Injector is to never fail silently and therefore only Explicit property injection is advised. This will gives the container the opportunity to use its Diagnostic Services and fail fast if the configuration is invalid.

As you can read in the referenced documentation, there are 2 ways of doing it property injection:

  1. By registering an initializer with a call to container.RegisterInitializer()
  2. By implementing IPropertySelectionBehavior

Option 2 will let Simple Injector check the dependencies using the Diagnostics Services automatically. When verifying all 'initializers' are executed also, but the code in lambda isn't checked for lifestyles etc.

Simple Injector does not encourage its users to take a dependency on the container and it therefore does not contain any Atrributes which you can use out-of-the-box such as Unity has the DependencyAttribute .

So you have 2 options here:

  1. Let project 1 also take a dependency on Unity (which it indirectly already has!, yuck) and create an implementation of IPropertySelectionBehavior to search for the Unity DependencyAttribute
  2. Create your own custom Attribute in project 2 and annotate _serviceRepo which this custom attribute also.

The only difference between 1 and 2 is which attribute is searched for. If you can't touch project 2, option 2 isn't possible. Implementing IPropertySelectionBehavior is straightforward:

// custom attribute only for option 1
public class ImportAttribute : Attribute { }

class UnityDependencyPropertySelectionBehavior : IPropertySelectionBehavior 
{
     public bool SelectProperty(Type type, PropertyInfo prop) 
     {
         return prop.GetCustomAttributes(typeof(DependencyAttribute)).Any();
     }
}

You can configure the container using this custom IPropertySelectionBehavior by:

container.Options.PropertySelectionBehavior = 
               new UnityDependencyPropertySelectionBehavior();

To summarize: Move the dependency to the constructor as it is not a optional dependency. In other words use constructor injection! If there are compelling reasons not to do this implement IPropertySelectionBehavior

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM