8

I have a ASP .Net Web API controller that I want to take 2 parameters. The first one is an EF context and the second being a caching interface. If I just have the EF context the constructor gets called, but when I add the caching interface I get the error:

An error occurred when trying to create a controller of type 'MyV1Controller'. Make sure that the controller has a parameterless public constructor.

private MyEntities dbContext;
private IAppCache cache;

public MyV1Controller(MyEntities ctx, IAppCache _cache)
{
     dbContext = ctx;
     cache = _cache;
}

My UnityConfig.cs

public static void RegisterTypes(IUnityContainer container)
{
    // TODO: Register your types here
    container.RegisterType<MyEntities, MyEntities>();
    container.RegisterType<IAppCache, CachingService>();
}

I would expect that Entity now knows about both types when a request is made for MyV1Controller function it should be able to instantiate an instance since that constructor takes types it knows about but that's not the case. Any idea why?

[EDIT] Note that I created my own class (IConfig) and registered it and add it to the constructor and it worked, but whenever I try to add the IAppCache to my constructor and make a request to the API I get the error telling me it can't construct my controller class. The only difference that I see is the IAppCache isn't in my projects namespace because it's a 3rd party class but that shouldn't matter from what I understand.

Here are the constructors for CachingService

public CachingService() : this(MemoryCache.Default) { } 

public CachingService(ObjectCache cache) { 
    if (cache == null) throw new ArgumentNullException(nameof(cache)); 
    ObjectCache = cache; 
    DefaultCacheDuration = 60*20; 
}
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
user441521
  • 6,942
  • 23
  • 88
  • 160
  • Is the class registration meant to be a singleton? Also check the `IAppCache`implementation `CachingService` to make sure that the class is not throwing any exception when initialized. that parameterless exception is the default message when an error occurs while trying to create controllers. – Nkosi Dec 02 '16 at 17:58
  • What are the dependencies of `CachingService` You mention that it is a 3rd party interface/class. it could be requesting a dependency that the container does not know about. – Nkosi Dec 02 '16 at 18:13
  • It uses MemoryCache.Default: https://github.com/alastairtree/LazyCache/blob/master/LazyCache/CachingService.cs – user441521 Dec 02 '16 at 18:18
  • Just create the parameter less constructor in your controller eg. public MyV1Controller() { } – Mysterion Dec 09 '16 at 13:33

3 Answers3

11

Check the IAppCacheimplementation CachingService to make sure that the class is not throwing any exception when initialized. that parameterless exception is the default message when an error occurs while trying to create controllers. It is not a very useful exception as it does not accurately indicate what the true error was that occurred.

You mention that it is a 3rd party interface/class. It could be requesting a dependency that the container does not know about.

Referencing Unity Framework IoC with default constructor

Unity is calling the constructor with the most parameters which in this case is...

public CachingService(ObjectCache cache) { ... }

As the container know nothing about ObjectCache it will pass in null which according to the code in the constructor will throw an exception.

UPDATE:

Adding this from comments as it can prove useful to others.

container.RegisterType<IAppCache, CachingService>(new InjectionConstructor(MemoryCache.Default));

Reference here Register Constructors and Parameters for more details.

Community
  • 1
  • 1
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • This is the implementation: public CachingService() : this(MemoryCache.Default) { } public CachingService(ObjectCache cache) { if (cache == null) throw new ArgumentNullException(nameof(cache)); ObjectCache = cache; DefaultCacheDuration = 60*20; } – user441521 Dec 02 '16 at 18:13
  • well then my guess would be that `cache==null` is `true`. – Nkosi Dec 02 '16 at 18:15
  • If I create an instance of CachingService() inside UnityConfig:RegisterTypes() it's not null so not seeing why it would be null when my controller is created at a later point in the application life. Is there anyway to hook into Unity while it's doing these creations of objects for the controller ctor to get a better look? – user441521 Dec 02 '16 at 18:18
  • Debug and put a break point in the constructor to see when it is being called. that should give you a look. – Nkosi Dec 02 '16 at 18:19
  • My controller constructor? It doesn't get there. If you're talking about the caching lib I have the assembly not the code. Do you mean some other place? – user441521 Dec 02 '16 at 18:28
  • "Unit is calling the constructor with the most parameters which in this case is" Why would it do that? How can I make it call the ctor with no param? – user441521 Dec 02 '16 at 18:31
  • 1
    I just did the following and it worked! container.RegisterType(new InjectionConstructor(MemoryCache.Default)); still not sure why Unity wasn't trying to use the parameterless ctor – user441521 Dec 02 '16 at 18:32
  • 1
    What you did is correct. You figured it out before I had a chance to respond. `still not sure why Unity wasn't trying to use the parameterless ctor` - that is how it was designed. it is pretty standard with most IoC containers. – Nkosi Dec 02 '16 at 18:34
  • You got the solution to move forward, but if you expect unity to provide dependencies then you should bind ObjectCache to MemoryCache.Default so that unity knows what to inject when you ask for object cache and remove your hard coded constructor injection. what you have done is logically equivalent to "new" and you are taking out the possiblity of injecting different implementations of ObjectCache. – Gurpreet Dec 07 '16 at 21:45
3

Most of the DI containers while trying to resolve a type always look for a constructor with maximum number of parameters. That is the reason why CachingService(ObjectCache cache) constructor was being invoked by default. As ObjectCache instance is not registered with Unity, so the resolution fails. Once you force the type registration to invoke specific constructor, everything works.

So if you register IAppCache and force it to invoke CachingService() - parameter less constructor, it will work as expected.

container.RegisterType<IAppCache, CachingService>(new InjectionConstructor());

Registering it this way, will force the parameter less constructor to be invoked and internally it will fall back on whatever the third part library wants to use as default. In your case it will be

CachingService() : this(MemoryCache.Default)

Another option that was mentioned in other answers is to register and pass the constructor parameter your self.

container.RegisterType<IAppCache, CachingService>(new InjectionConstructor(MemoryCache.Default));

This will also work, but here you are taking the responsibility of supplying the cache provider. In my opinion, I would rather let the third party library handle its own defaults instead of me as a consumer taking over that responsibility.

Please take a look at How does Unity.Resolve know which constructor to use?

And few additional information for Niject https://github.com/ninject/ninject/wiki/Injection-Patterns

If no constructors have an [Inject] attribute, Ninject will select the one with the most parameters that Ninject understands how to resolve.

Community
  • 1
  • 1
Vinod
  • 1,882
  • 2
  • 17
  • 27
0

For LazyCache version 2.1.2 (maybe even earlier) the existing solution no longer works (no constructor that receives MemoryCache), but it works as simple as:

container.RegisterType<IAppCache, CachingService>(new InjectionConstructor());

This worked with .NET Framework 4.6.1, Unity Abstractions 3.1.0.

Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164