Ninject with Web Api, SignalR, MVC and OWIN - asp.net

I am using a Ninject DI in my web application with a bunch of technoligies from Asp.Net stack (MVC, Web Api 2, SignalR).
I have managed to make DI work for all technologies in use with the following approach:
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
internal static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
// Binding services here
}
}
So far so good.
All this was working with Web Api being initialized in Global.asax.
Now I'm switching to OWIN pipeline. So I removed GlobalConfiguration.Configure(WebApiConfig.Register); from Global.asax and added
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
to my OwinStartup class. DI for Web Api stoped working.
I started searching for appropriate solution and found Ninject.Web.WebApi.OwinHost package. So in order to have a single Kernel resolving dependencies for all technologies, I've made the following changes:
in OwinStartup:
app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
app.UseNinjectWebApi(config);
in NinjectWebCommon:
//[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(App.TradingServer.ConfiguratorApp.App_Start.NinjectWebCommon), "Start")]
//[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(App.TradingServer.ConfiguratorApp.App_Start.NinjectWebCommon), "Stop")]
These lines were disabled to avoid initializing kernel twice.
This fixed DI for Web Api but not for SignalR. When client tries to connect to hub I get the following exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.AspNet.SignalR.PersistentConnection.ProcessNegotiationRequest(HostContext context)
at Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequest(HostContext context)
at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.ProcessRequest(HostContext context)
at Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequest(IDictionary`2 environment)
at Microsoft.AspNet.SignalR.Owin.Middleware.HubDispatcherMiddleware.Invoke(IOwinContext context)
at Microsoft.Owin.Infrastructure.OwinMiddlewareTransition.Invoke(IDictionary`2 environment)
at Microsoft.Owin.Mapping.MapMiddleware.<Invoke>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at System.Web.Http.Owin.HttpMessageHandlerAdapter.<InvokeCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Ninject.Web.Common.OwinHost.OwinBootstrapper.<Execute>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContextStage.<RunApp>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.<DoFinalWork>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Owin.Host.SystemWeb.Infrastructure.ErrorState.Rethrow()
at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar)
at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar)
at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) | RuntimeMethodInfo.UnsafeInvokeInternal => RuntimeMethodHandle.InvokeMethod => Application.Application_Error
I am kind of lost. I'we read around two dozens of articles but none of them gave me the solution. Would appreciate any help.
My final aim is to have a single kernel that serves Web Api, MVC and SignalR and supports OWIN pipeline.
Edit: Since I've got a comment that my case might be a duplicate of another question I believe I need to give some explanations.
I have three scenarios.
WebApi initialization in Global.asax with GlobalConfiguration.Configure(WebApiConfig.Register), Ninject initialization with NinjectWebCommon and Bootstrapper.
This gives me both injection in WebApi and SignalR. But since I would like to move WebApi initialization to OWIN startup this approach is obsolete.
WebApi initialization with OWIN Startup, Ninject initialization with NinjectWebCommon and Bootstrapper.
SignalR injection works, WebApi injection does not work.
WebApi initialization with OWIN Startup, Ninject initialization with UseNinjectMiddleware, UseNinjectWebApi.
WebApi injection works, SignalR injection does not work.
So basically I need to figure out how to put this together so that both WebApi and SignalR injection work when I initialize WebApi on OWIN pipeline.
The code for NinjectWebCommon is in original question below. It contains code for creating SignalR resolver but it does not help in scenario 3.
Edit 2: After a couple of hours of trial and error method I came to a conclusion that calling
app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
app.UseNinjectWebApi(config);
conflicts with this call:
GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
So problem description narrows to this. When I use the following pattern SignalR stops working:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseNinjectMiddleware(CreateKernel);
app.UseNinjectWebApi(config);
GlobalHost.HubPipeline.AddModule(new GlobalSignalRExceptionHandler());
app.MapSignalR();
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
DependencyResolver.SetResolver(new Ninject.Web.Mvc.NinjectDependencyResolver(kernel));
return kernel;
}
But if I comment the line
//GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
SignalR starts working again. But no injection inside hubs of course.

Finally I managed to get the working Ninject configuration that supports OWIN pipe, WebApi, MVC and SignalR.
By the time when I posted the question I had a work-around (which was disabling DI in SignalR hubs) so I decided to waste no more time on this and moved on.
But when I tried running OWIN in-memory Test Server with my Startup class it occurred that DI was not working. CreateKernel method was called too late which resulted in creating several instances of an object that was used in sengleton scope.
After playing with different variations of initialization I've made DI work for OWIN Test Server and it also fixed the SignalR DependencyResolver.
The Solution:
I stopped using packages Ninject.Web.Common.OwinHost and Ninject.Web.WebApi.OwinHost so these calls were removed from my Configuration method:
//app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
//app.UseNinjectWebApi(config);
Instead I do the following:
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
NinjectWebCommon.Start();
config.DependencyResolver = new NinjectDependencyResolver(NinjectWebCommon.bootstrapper.Kernel);
app.UseWebApi(config);
app.MapSignalR();
}
public static class NinjectWebCommon
{
private static bool _isStarted;
internal static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
// When creating OWIN TestService instances during unit tests
// Start() method might be called several times
// This check ensures that Ninject kernel is initialized only once per process
if (_isStarted)
return;
_isStarted = true;
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
internal static IKernel CreateKernel()
{
var kernel = new StandardKernel();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
// DI for SignalR
GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
// DI for MVC
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
// Binding code here
kernel.Bind<Something>().ToSelf().InSingletonScope();
}
}

in order to use a dependency resolver for both WebApi and SignalR you need to implement a class that looks like this:
public class NinjectDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver,
System.Web.Http.Dependencies.IDependencyResolver
{
public readonly IKernel Kernel;
public NinjectDependencyResolver(string moduleFilePattern)
: base()
{
Kernel = new StandardKernel();
Kernel.Load(moduleFilePattern);
}
public override object GetService(Type serviceType)
{
var service = Kernel.TryGet(serviceType) ?? base.GetService(serviceType);
return service;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
IEnumerable<object> services = Kernel.GetAll(serviceType).ToList();
if (services.IsEmpty())
{
services = base.GetServices(serviceType) ?? services;
}
return services;
}
public System.Web.Http.Dependencies.IDependencyScope BeginScope()
{
return this;
}
public void Dispose()
{ }
}
then in your startup class you should register NinjectDependencyResolver for both WebApi and SignalR, like this:
public void Configuration(IAppBuilder app)
{
var dependencyResolver = new NinjectDependencyResolver("*.dll");
var httpConfiguration = new HttpConfiguration();
httpConfiguration.DependencyResolver = dependencyResolver;
app.UseWebApi(httpConfiguration);
var hubConfig = new HubConfiguration { Resolver = dependencyResolver };
app.MapSignalR(hubConfig);
}

SignalR must be configured after your dependency injection configuration. So, in your OWIN Startup class, make sure that app.MapSignalR() is called after setting the MVC dependency resolver (System.Web.MVC.DependencyResolver), the WebApi dependency resolver (System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver) and the SignalR dependency resolver (Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver).

Related

Use WebApplicationFactory to test CoreWCF endpoints with AutoFac DI

I have a CoreWCF project with Autofac DI and I want to create some Integration Tests using a custom WebApplicationfactory.
I have used the custom WebApplicationFactory code for Autofac suggested here
https://github.com/autofac/Autofac/issues/1207 by Alistair Evans
This works fine for WebApi endpoints but it does not work for my CoreWCF endpoints.
What I found was that the Microsoft.AspNetCore.TestHost.TestServer that is created does not define the Features property with any IServerAddressesFeature. This causes a problem when the CoreWCF.ServiceObjectModel ApplyConfiguration is executed.
I have got around this by using reflection to set the Features property in the CreateServiceProvider method of the CustomServiceProviderFactory as shown below but does anybody know if there is a better way around this.
/// Enables in-memory integration testing for CoreWCF (outside-in testing via <see cref="HttpClient"/>).
///
/// Use these tests to exercise the entire HTTP stack, rather than create in-process ServiceModel channels.
///
/// <see href="https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1"/>
/// <seealso href="https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1"/>
/// </summary>
/// <typeparam name="TStartup"></typeparam>
public class WcfWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
private ILifetimeScope _container;
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestContainer<ContainerBuilder>(container =>
{
container.RegisterBuildCallback(BuildCallback);
});
builder.ConfigureTestServices(services =>
{
});
}
private void BuildCallback(ILifetimeScope obj)
{
_container = obj;
}
protected override IHost CreateHost(IHostBuilder builder)
{
builder.UseServiceProviderFactory(new CustomServiceProviderFactory(ClientOptions.BaseAddress.ToString()));
return base.CreateHost(builder);
}
protected override IHostBuilder? CreateHostBuilder()
{
return base.CreateHostBuilder();
}
}
/// <summary>
/// Based upon https://github.com/dotnet/aspnetcore/issues/14907#issuecomment-620750841 - only necessary because of an issue in ASP.NET Core
/// </summary>
public class CustomServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
{
private readonly string _testServerBaseAddress;
private AutofacServiceProviderFactory _wrapped;
private IServiceCollection _services;
public CustomServiceProviderFactory(string testServerBaseAddress)
{
_testServerBaseAddress = testServerBaseAddress;
_wrapped = new AutofacServiceProviderFactory();
}
public ContainerBuilder CreateBuilder(IServiceCollection services)
{
// Store the services for later.
_services = services;
return _wrapped.CreateBuilder(services);
}
public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
{
var sp = _services.BuildServiceProvider();
#pragma warning disable CS0612 // Type or member is obsolete
var filters = sp.GetRequiredService<IEnumerable<IStartupConfigureContainerFilter<ContainerBuilder>>>();
#pragma warning restore CS0612 // Type or member is obsolete
foreach (var filter in filters)
{
filter.ConfigureContainer(b => { })(containerBuilder);
}
var serviceProvider = _wrapped.CreateServiceProvider(containerBuilder);
// The CoreWCF.ServiceObjectModel ApplyConfiguration expects there to be at least one IServerAddressesFeature
// This is normally present for the Kestrel webserver but for the TestServer in the Microsoft.AspNetCore.TestHost namespace does not seem to populate it
// Had to use Refelction to set this since the Set method causes a containerBuilder Build operation
var addresses = new ServerAddressesFeature();
addresses.Addresses.Add(_testServerBaseAddress);
var features = new FeatureCollection();
features.Set<IServerAddressesFeature>(addresses);
var testServer = serviceProvider.GetRequiredService<IServer>();
var field = typeof(TestServer).GetField("<Features>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(testServer, features);
return serviceProvider;
}
}
}
I have found some clue in https://stackoverflow.com/a/48547597/3299257
Specifically in the announcement
Hosting no longer adds default server address when no address is explicitly configured
The WebHost will no longer add the default server address of http://localhost:5000 to the IServerAddressesFeature when none is specified. The configuration of the default server address will now be a responsibility of the server.
Addresses specified in IServerAddressesFeature are intended to be used by servers as a fallback when no explicit address is specified directly.
Adding feature was discussed here https://github.com/dotnet/aspnetcore/issues/5931 and came to conclusion that it won't be implemented in TestServer.
In my test scenario I do not use full WebApplicationFactory, but rather directly utilize TestServer so I could make a simpler soultion:
var addresses = new ServerAddressesFeature();
addresses.Addresses.Add("http://localhost");
var features = new FeatureCollection();
features.Set<IServerAddressesFeature>(addresses);
return new TestServer(builder, features);
In your scenario:
(sorry, this is not verified), you could use WebApplicationFactory.WithWebHostBuilder and then, invoke .UseTestServer constructing your own TestServer and providing feature collection in the second argument.
factory.WithWebHostBuilder(builder => {
var addresses = new ServerAddressesFeature();
addresses.Addresses.Add("http://localhost");
var features = new FeatureCollection();
features.Set<IServerAddressesFeature>(addresses);
builder.UseTestServer(new TestServer(builder, features));
});

Getting parameterles public constructor error

An error occurred when trying to create a controller of type
'ChatBotController'. Make sure that the controller has a
parameterless public constructor.
at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType) ↵ at
System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage
request) ↵ at
System.Web.Http.Dispatcher.HttpControllerDispatcher.d__15.MoveNext()
When I try to reach my IFeedbackRepository I get the error aboe. It happens when I put in the constructor in my ChatBotController.cs
public class ChatBotController : ApiController
{
IFeedbackRepository _feedbackRepository;
public ChatBotController(IFeedbackRepository feedbackRepository)
{
_feedbackRepository = feedbackRepository;
}
[HttpPost]
public IHttpActionResult PostQuestion([FromBody]string message) //TODO: make sure that webapi will search the message in the body of the http request
{
throw new NotImplementedException();
}
}
I'm using both MVC and Api which I both resolve in my Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
DependencyConfig.RegisterWebApiDependencies();
DependencyConfig.RegisterMvcDependencies();
}
This is my DependencyConfig.cs for both MVC and Api:
public static void RegisterWebApiDependencies()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<IAnswerGenerator, PxlAnswerGenerator>(Lifestyle.Scoped);
container.Register<ChatBotDbContext>(Lifestyle.Scoped);
container.Register<IFeedbackRepository, FeedbackDbRepository>(Lifestyle.Scoped);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
public static void RegisterMvcDependencies()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.Register<IFeedbackRepository, FeedbackDbRepository>(Lifestyle.Scoped);
container.Register<ChatBotDbContext>(Lifestyle.Scoped);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
What am I doing wrong?
According to the documentation of Simple-Injector when you want to initialize the resolver for the WebApi part of your registration you need to call
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorWebApiDependencyResolver(container));
I don't see you calling container.RegisterWebApiControllers(GlobalConfiguration.Configuration); in RegisterWebApiDependencies(). This is required.
You may want to review the simpleinjector documentation for integrating with ASP.NET Web API and MVC here:
https://simpleinjector.readthedocs.io/en/latest/webapiintegration.html
Also the documentation above has the container/DI setup at the beginning of application_start(). If the above change alone does not work, you may want to try putting the following two lines at the start of application_start():
DependencyConfig.RegisterWebApiDependencies();
DependencyConfig.RegisterMvcDependencies();

Configuring ASP.Net Identity for an ASP.Net WebAPI application

I have a SPA ASP.NET WebAPI application which previously allowed anonymous access. I have now configured ASP.Net Identity for it but I cannot get the Identity related controllers and my application's other controllers to work at the same time :-(
It's either one or the other!
I have added the startup class to my project:
using Test.MyProject;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Serialization;
using Owin;
using System;
using System.Configuration;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Http;
[assembly: OwinStartup(typeof(Test.Client.Startup))]
namespace Test.Client
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureWebApi(httpConfig);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
//app.UseWebApi(httpConfig); // If this line is commented out my application's controllers work. But then my Account Controller does't work. It if is included, my application's controllers don't work, whilst the Account Controller work
GlobalConfiguration.Configure(WebApiConfig.Register);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
private void ConfigureWebApi(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
}
And I have added controllers for managing Users and Roles.
The statement GlobalConfiguration.Configure(WebApiConfig.Register) was previously in the application start event in global.aspx.cs but now moved to the startup class to have everything in the same place.
The WebApiConfig.Register method looks like this:
public static void Register(HttpConfiguration config)
{
var container = new UnityContainer();
// Web API configuration and services
string appStorageProvider = ConfigurationManager.AppSettings["StorageProvider"];
var provider =(TestComposition.StorageProvider) Enum.Parse(typeof (TestComposition.StorageProvider), appStorageProvider, true);
TestComposition.Setup(container, provider);
container.RegisterType<GeneralLogger, GeneralLogger>();
container.RegisterType<IExceptionLogger, ExceptionLogger>();
config.EnableCors();
config.DependencyResolver = new UnityResolver(container);
config.Services.Add(typeof (IExceptionLogger), container.Resolve<GeneralLogger>());
// Web API routes
config.MapHttpAttributeRoutes();
}
In my new AccountController I have code which allows me to retrieve the ApplicationUserManager from the OwinContext set up in the Startup class.
protected ApplicationUserManager AppUserManager
{
get
{
return _AppUserManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
With app.UseWebApi(httpConfig) commented out as shown above my application works as it used to. But if I invoke any action on my new AccountController I get this:
Request.GetOwinContext() error CS1061: 'HttpRequestMessage' does not
contain a definition for 'GetOwinContext' and no extension method
'GetOwinContext' accepting a first argument of type
'HttpRequestMessage' could be found (are you missing a using directive
or an assembly reference?)
If I comment in the app.UseWebApi(httpConfig) statement the AccountController works but then my other controllers don't work. Here I get errors like these:
{
"message": "An error has occurred.",
"exceptionMessage": "An error occurred when trying to create a controller of type 'TestController'. 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.d__1.MoveNext()",
"innerException": {
"message": "An error has occurred.",
"exceptionMessage": "Type 'MyProject.Api.TestController' 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)"
} }
Any idea what is going on here?
The problem is that you are not using the same HttpConfiguration instance during start up for configuring WebApi with OWIN.
This way your OWIN Web API middleware has no knowledge of UnityContainer, and will use its default implementation. Because of this the creation of your controllers failed.
Please use the same HttpConfiguration for both Web Api configuration and UnityContainer registration:
public class Startup {
public void Configuration(IAppBuilder app) {
ConfigureOAuthTokenGeneration(app);
ConfigureWebApi(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app) {
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
private void ConfigureWebApi(IAppBuilder app) {
// configure Web Api
GlobalConfiguration.Configure(WebApiConfig.Register);
// Manually assign httpConfig from GlobalConfiguration
HttpConfiguration httpConfig = GlobalConfiguration.Configuration;
// Use same config with OWIN app
app.UseWebApi(httpConfig);
}
}
You are configuring web api in multiple places. The WebApiConfig.Register method should consolidate everything you want configured for the HttpConfiguration:
public static void Register(HttpConfiguration config) {
var container = new UnityContainer();
// Web API configuration and services
string appStorageProvider = ConfigurationManager.AppSettings["StorageProvider"];
var provider =(TestComposition.StorageProvider) Enum.Parse(typeof (TestComposition.StorageProvider), appStorageProvider, true);
TestComposition.Setup(container, provider);
container.RegisterType<GeneralLogger, GeneralLogger>();
container.RegisterType<IExceptionLogger, ExceptionLogger>();
config.EnableCors();
config.DependencyResolver = new UnityResolver(container);
config.Services.Add(typeof (IExceptionLogger), container.Resolve<GeneralLogger>());
// Web API routes
config.MapHttpAttributeRoutes();
// configure formatter
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}

Autofac OWIN TestServer and HttpContext

I am trying to setup Integration tests with my IIS Hosted WebAPI 2.2 application. I use Autofac for DI and I am using the new ASP.net Identity stack which uses OWIN. I am running into an issue with Autofac where the HttpContext class is always null. Here is how I am setting up my base integration test class-
[TestClass]
public class TestBase
{
private SimpleLifetimeScopeProvider _scopeProvider;
private IDependencyResolver _originalResolver;
private HttpConfiguration _configuration;
public TestServer Server { get; private set; }
[TestInitialize]
public void Setup()
{
Server = TestServer.Create(app =>
{
//config webpai
_configuration = new HttpConfiguration();
WebApiConfig.Register(_configuration);
// Build the container.
var container = App_Start.IocConfig.RegisterDependencies(_configuration);
_scopeProvider = new SimpleLifetimeScopeProvider(container);
//set the mvc dep resolver
var mvcResolver = new AutofacDependencyResolver(container, _scopeProvider);
_originalResolver = DependencyResolver.Current;
DependencyResolver.SetResolver(mvcResolver);
//set the webapi dep resolvers
_configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(_configuration);
app.UseAutofacMvc();
});
}
[TestCleanup]
public void Cleanup()
{
// Clean up the fake 'request' scope.
_configuration.Dispose();
DependencyResolver.SetResolver(_originalResolver);
_scopeProvider.EndLifetimeScope();
Server.Dispose();
}
}
When a simple test starts, I get an ArgumentNullException "Value cannot be null" httpContext. Which if I track down into the autofac code, I think it is coming from this extension method -
public static class AutofacMvcAppBuilderExtensions
{
internal static Func<HttpContextBase> CurrentHttpContext = () => new HttpContextWrapper(HttpContext.Current);
/// <summary>
/// Extends the Autofac lifetime scope added from the OWIN pipeline through to the MVC request lifetime scope.
/// </summary>
/// <param name="app">The application builder.</param>
/// <returns>The application builder.</returns>
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public static IAppBuilder UseAutofacMvc(this IAppBuilder app)
{
return app.Use(async (context, next) =>
{
var lifetimeScope = context.GetAutofacLifetimeScope();
var httpContext = CurrentHttpContext();
if (lifetimeScope != null && httpContext != null)
httpContext.Items[typeof(ILifetimeScope)] = lifetimeScope;
await next();
});
}
}
loacted in the Core/Source/Autofac.Integration.Mvc.Owin/AutofacMvcAppBuilderExtensions.cs file. Is there a problem with my setup, or a proper way to use Autofac in integration tests with a WebApi application using IIS Host and OWIN Middleware?
It appears you already asked this as an issue over on the Autofac project. I'll copy/paste the answer here (though in the future it'd probably be better to go with one or the other and not both).
Part of the awesomeness of OWIN-only apps is that you don't need HttpContext anymore. Nothing is tied to that; instead, it's all HttpContextBase and things that are separate from the legacy IIS. Like, in Web API, the current context is always shipped around with the HttpRequestMessage - there's no global static HttpContext.Current because that's legacy stuff.
Thus, when you run unit tests with an OWIN test host, you can expect there to not be an HttpContext.Current. It's decoupled from all that.
MVC can't run as OWIN-only because the libraries are tightly coupled to the legacy IIS/ASP.NET stack. Trying to test MVC stuff using an OWIN-only test server is going to give you trouble like this. That will change with the new ASP.NET 5.0 coming out with the new Visual Studio.
If you need to test MVC in an integrated way, there isn't a way to do that with OWIN right now. You have to fire up IIS Express.
Finally, I do see that you're missing the Web API middleware for OWIN (the actual Microsoft Web API middleware). That might give you other problems down the line.
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(_configuration);
app.UseAutofacMvc();
// You're missing this:
app.UseWebApi(config);

Occasional exception on startup with SignalR and ASP.NET MVC 3 on IIS Express

I'm writing an internal web app for our server software that uses ASP.NET MVC 3 and SignalR 0.5.2. We're occasionally seeing the following exception on startup:
System.InvalidOperationException: This method cannot be called during the application's pre-start initialization stage.
Server stack trace:
at System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled()
at System.Web.Compilation.BuildManager.GetReferencedAssemblies()
at SignalR.Hosting.AspNet.Infrastructure.AspNetAssemblyLocator.GetAssemblies()
at SignalR.Hubs.ReflectedHubDescriptorProvider.BuildHubsCache()
at System.Lazy`1.CreateValue()
Exception rethrown at [0]:
at System.Lazy`1.get_Value()
at SignalR.Hubs.ReflectedHubDescriptorProvider.TryGetHub(String hubName, HubDescriptor& descriptor)
at SignalR.Hubs.DefaultHubManager.<>c__DisplayClass1.<GetHub>b__0(IHubDescriptorProvider p)
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at SignalR.Hubs.DefaultHubManager.GetHub(String hubName)
at SignalR.Hubs.HubManagerExtensions.EnsureHub(IHubManager hubManager, String hubName)
at SignalR.Hubs.HubDispatcher.GetSignals(ClientHubInfo hubInfo, String connectionId)
at SignalR.Hubs.HubDispatcher.<>c__DisplayClass23.<CreateConnection>b__22(ClientHubInfo info)
at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
at System.Linq.Enumerable.<ConcatIterator>d__71`1.MoveNext()
at System.Collections.Generic.HashSet`1.UnionWith(IEnumerable`1 other)
at System.Collections.Generic.HashSet`1..ctor(IEnumerable`1 collection, IEqualityComparer`1 comparer)
at SignalR.Connection..ctor(IMessageBus messageBus, IJsonSerializer jsonSerializer, String baseSignal, String connectionId, IEnumerable`1 signals, IEnumerable`1 groups, ITraceManager traceManager)
at SignalR.Hubs.HubDispatcher.CreateConnection(String connectionId, IEnumerable`1 groups, IRequest request)
at SignalR.PersistentConnection.ProcessRequestAsync(HostContext context)
at SignalR.Hubs.HubDispatcher.ProcessRequestAsync(HostContext context)
at SignalR.Hosting.AspNet.AspNetHandler.ProcessRequestAsync(HttpContextBase context)
at SignalR.Hosting.AspNet.HttpTaskAsyncHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
I suspect this happens because there is a browser open while we're restarting IIS Express and it's making a request (via the SignalR javascript) at the exact moment between when the hub routes are mapped and the initialization finishes. I could be entirely wrong about the cause, we have unfortunately not been able to reproduce this reliably and this is my first ASP.NET project so I'm pretty lost as to what could be happening.
This is our NinjectWebCommon.cs file in the App_Start folder:
[assembly: WebActivator.PreApplicationStartMethod(typeof(WebStatusClient.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(WebStatusClient.App_Start.NinjectWebCommon), "Stop")]
namespace WebStatusClient.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
using SignalR.Infrastructure;
using SignalR.Ninject;
using SignalR;
using System.Web.Routing;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
public static StandardKernel Kernel { get; private set; }
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
RouteTable.Routes.MapHubs();
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
Kernel = new StandardKernel();
Kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
Kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(Kernel);
return Kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<Impl.StatusForwarder>()
.To<Impl.StatusForwarder>()
.InSingletonScope();
kernel.Bind<StatusInterfaceClient.IStatusNotificationTarget>()
.ToMethod(context => context.Kernel.Get<Impl.StatusForwarder>());
// Set the SignalR dependency injector.
SignalR.GlobalHost.DependencyResolver = new SignalR.Ninject.NinjectDependencyResolver(kernel);
// Force the creation of the status manager.
Impl.StatusInterfaceManager.Initialize();
}
}
}
Any suggestions for figuring out what could be going wrong? Should I be mapping the hubs later in the initialization process?

Resources