I'm exploring the use of DryIoc in a .NET WebAPI application and have noticed a strange behavior with the initialization steps. In a simple test webapi application, I have the following DryIoc registration class which gets called immediately after the WebApi config registration.
public class DryIocConfig
{
public static void Register(HttpConfiguration config)
{
var c = new Container().WithWebApi(config);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
}
And the following WebApi controller:
public class ValuesController : ApiController
{
private readonly IWidgetService _widgetService;
public ValuesController(IWidgetService widgetService)
{
_widgetService = widgetService;
}
// GET api/values
public IEnumerable<Widget> Get()
{
return _widgetService.GetWidgets();
}
}
This seems to work fine, but in experimenting with was seems to me to be the identical, but written a little more verbose, code I get an error.
public class DryIocConfig
{
public static void Register(HttpConfiguration config)
{
var c = new Container();
c.WithWebApi(config); // Now separate statement rather than chained.
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
}
The exception I get is the following (as JSON):
{
"Message" : "An error has occurred.",
"ExceptionMessage" : "An error occurred when trying to create a controller of type 'ValuesController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType" : "System.InvalidOperationException",
"StackTrace" : " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
"InnerException" : {
"Message" : "An error has occurred.",
"ExceptionMessage" : "Type 'IOCContainerTest.DryLoc.Controllers.ValuesController' does not have a default constructor",
"ExceptionType" : "System.ArgumentException",
"StackTrace" : " at System.Linq.Expressions.Expression.New(Type type)\r\n at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}
}
Is this something odd with DryIoc, or is this a C# nuance that I've just never come across?
This is because .WithWebApi()
is an extension method per the source.
public static IContainer WithWebApi(this IContainer container, HttpConfiguration config,
IEnumerable<Assembly> controllerAssemblies = null, IScopeContext scopeContext = null,
Func<Type, bool> throwIfUnresolved = null)
{
container.ThrowIfNull();
if (container.ScopeContext == null)
container = container.With(scopeContext: scopeContext ?? new AsyncExecutionFlowScopeContext());
container.RegisterWebApiControllers(config, controllerAssemblies);
container.SetFilterProvider(config.Services);
InsertRegisterRequestMessageHandler(config);
config.DependencyResolver = new DryIocDependencyResolver(container, throwIfUnresolved);
return container;
}
In the first syntax example, you create a new instance of Container
and pass that newly created instance to .WithWebApi()
. This in turn updates the container instance and finally returns it back to variable c
.
In the second syntax example, you are never returning the value of the extension method BACK to the original variable, updating it. You are calling it as if it were a void method, which does nothing in this case, hence the exception.
If you had instead written:
var c = new Container();
c = c.WithWebApi(config);
it would have essentially been a more verbose example of the first syntax and would have properly updated c
with the new functionality.