autofac webapi owin integration problems with middleware exection order not correct - autofac

I'm confused with Autofac Examples : WebApiExample.OwinSelfHost, the startup class is following:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// In OWIN you create your own HttpConfiguration rather than
// re-using the GlobalConfiguration.
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional });
var builder = new ContainerBuilder();
// Register Web API controller in executing assembly.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL - Register the filter provider if you have custom filters that need DI.
// Also hook the filters up to controllers.
builder.RegisterWebApiFilterProvider(config);
builder.RegisterType<CustomActionFilter>()
.AsWebApiActionFilterFor<TestController>()
.InstancePerRequest();
// Register a logger service to be used by the controller and middleware.
builder.Register(c => new Logger()).As<ILogger>().InstancePerRequest();
// Autofac will add middleware to IAppBuilder in the order registered.
// The middleware will execute in the order added to IAppBuilder.
builder.RegisterType<FirstMiddleware>().InstancePerRequest();
builder.RegisterType<SecondMiddleware>().InstancePerRequest();
// Create and assign a dependency resolver for Web API to use.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// The Autofac middleware should be the first middleware added to the IAppBuilder.
// If you "UseAutofacMiddleware" then all of the middleware in the container
// will be injected into the pipeline right after the Autofac lifetime scope
// is created/injected.
//
// Alternatively, you can control when container-based
// middleware is used by using "UseAutofacLifetimeScopeInjector" along with
// "UseMiddlewareFromContainer". As long as the lifetime scope injector
// comes first, everything is good.
app.UseAutofacMiddleware(container);
// Again, the alternative to "UseAutofacMiddleware" is something like this:
// app.UseAutofacLifetimeScopeInjector(container);
// app.UseMiddlewareFromContainer<FirstMiddleware>();
// app.UseMiddlewareFromContainer<SecondMiddleware>();
// Make sure the Autofac lifetime scope is passed to Web API.
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
The FirstMiddleware and SecondMiddleware code was as following:
public class FirstMiddleware : OwinMiddleware
{
private readonly ILogger _logger;
public FirstMiddleware(OwinMiddleware next, ILogger logger) : base(next)
{
this._logger = logger;
}
public override async Task Invoke(IOwinContext context)
{
this._logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name);
await Next.Invoke(context);
}
}
public class SecondMiddleware : OwinMiddleware
{
private readonly ILogger _logger;
public SecondMiddleware(OwinMiddleware next, ILogger logger) : base(next)
{
this._logger = logger;
}
public override async Task Invoke(IOwinContext context)
{
this._logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name);
await Next.Invoke(context);
}
}
According to the comments, the middleware registration order matters. FirstMiddleware first, then SecondMiddleware. but the output was second middleware was invoked first.
the program logs output here
What's wrong with the order?
This is the autofac official example.WebApiExample.OwinSelfHost

Looks like you've found a bug! I've filed an issue about it on your behalf. You can read more technical details about it there, but the short version is that over the years we've changed some Autofac internals to support .NET Core and this looks like something we've missed.
The workaround until this is fixed will be to register the middleware in reverse order, which isn't awesome because once the fix is applied you'll have to reverse them back. :(

Related

ASP.NET Core 2: Entity Framework Context is disposed too early in asynchronous PayPal IPN. How do I get it back in a later thread?

I have an endpoint which is receiving IPN activity from PayPal. Here is the POST Action that is taken straight from their docs with manual modifications:
[HttpPost]
public IActionResult Receive()
{
IPNContext ipnContext = new IPNContext()
{
IPNRequest = Request
};
using (StreamReader reader = new StreamReader(ipnContext.IPNRequest.Body, Encoding.ASCII))
{
ipnContext.RequestBody = reader.ReadToEnd();
}
List<KeyValuePair<string, string>> ipnVarsWithCmd = ipnContext.RequestBody.Split('&')
.Select(x => new KeyValuePair<string, string>(x.Split('=')[0], x.Split('=')[1])).ToList();
//Fire and forget verification task -- ** THIS **
Task.Run(() => VerifyTask(ipnContext, ipnVarsWithCmd));
//Reply back a 200 code
return Ok();
}
The issue is the indicated line. This is a "fire and forget" route, and is executed asynchronously. When the Action is complete, and returns Ok, I am assuming that the injected Entity Framework context from the controller:
public class IPNController : Controller
{
private readonly EFContext _context;
public IPNController(EFContext context)
{
_context = context;
}
}
... gets Disposed? According to my logs, it looks like it.
Meanwhile, I have that second thread doing the actual legwork of the IPN request which needs that EFContext to be around.
Is there a pattern I am missing here? (Bearing in mind whilst I'm not new to .NET I am to .NET Core)
Or is there a way I can "get it back" so I can use it?
Update:
You might find my initialisation of the context useful:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<EFContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
}
Change:
Task.Run(() => VerifyTask(ipnContext, ipnVarsWithCmd));
to
await Task.Run(() => VerifyTask(ipnContext, ipnVarsWithCmd));
and method declaration to:
public async Task<IActionResult> Receive()
Also wrap IPNContext to using block to let it dispose when it is not needed.

Using Autofac Interface Interception with IAsyncInterceptor

I did use this documentation:
https://autofaccn.readthedocs.io/en/latest/advanced/interceptors.html
to implement Interface Interceptors. To handle my async calls I used the IAsyncInterceptor interface described here:
https://github.com/JSkimming/Castle.Core.AsyncInterceptor
The registration code I came up with does look like this:
builder.Register(c => new CallResultLoggerInterceptor())
.Named<IAsyncInterceptor>("log-calls");
builder.RegisterType<AppointmentService>()
.As<IAppointmentService>()
.EnableInterfaceInterceptors()
.InstancePerDependency();
where the AppointmentService has an InterceptAttribute.
[Intercept("log-calls")]
public class AppointmentService : IAppointmentService
...
When i call the containers Build() method, it throws an ComponentNotRegisteredException with the message:
The requested service 'log-calls (Castle.DynamicProxy.IInterceptor)' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
which is correct because I do not implement IInterceptor but IAsyncInterceptor. I guess the problem is the concrete implementation of EnableInterfaceInterceptors in autofac using the "wrong" extension method of the ProxyGenerator - but how can I solve this?
Cheers,
Manuel
You need to register a named IInterceptor for Autofac interceptors to work. You're registering an IAsyncInterceptor. That won't work.
Note Autofac has no support for this extended async interceptor extension you're using. If you want to get that to work, it'll require writing a custom adapter of some nature to get it to respond to IInterceptor.
You can see my answer in the issue of Castle.Core.AsyncInterceptor:
https://github.com/JSkimming/Castle.Core.AsyncInterceptor/issues/42#issuecomment-592074447
create an adapter
public class AsyncInterceptorAdaper<TAsyncInterceptor> : AsyncDeterminationInterceptor
where TAsyncInterceptor : IAsyncInterceptor
{
public AsyncInterceptorAdaper(TAsyncInterceptor asyncInterceptor)
: base(asyncInterceptor)
{ }
}
create your async interceptor
public class CallLoggerAsyncInterceptor : AsyncInterceptorBase
{
....
}
relate the interceptor to interface
[Intercept(typeof(AsyncInterceptorAdaper<CallLoggerAsyncInterceptor>))]
public interface ISomeType
register to IoC container
//register adapter
builder.RegisterGeneric(typeof(AsyncInterceptorAdaper<>));
//register async interceptor
builder.Register(c => new CallLoggerAsyncInterceptor(Console.Out));
I've made a code sample in https://github.com/wswind/aop-learn/blob/master/AutofacAsyncInterceptor
I've created my own extension method for registering application services.
This extension method simply prepares input parameter for castle core ProxyGenerator.
using System;
using System.Collections.Generic;
using System.Linq;
using Castle.DynamicProxy;
using Autofac;
namespace pixi.Extensions
{
public static class AutofacExtensions
{
private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator();
/// <summary>
/// Use this extension method to register default interceptors <code>UnitOfWorkInterceptor</code>
/// and <code>LoggingInterceptor</code> on your application service implementations. If you need custom
/// interceptors that are not part of infrastructure but are part of specific business module then pass
/// in those interceptors in params explicitly.
/// </summary>
/// <param name="builder"></param>
/// <param name="interceptors"></param>
/// <typeparam name="TImplementation"></typeparam>
/// <typeparam name="TService"></typeparam>
/// <exception cref="ArgumentException"></exception>
public static void RegisterApplicationService<TImplementation, TService>(this ContainerBuilder builder, params Type[] interceptors)
where TImplementation : class
{
ValidateInput<TService>(interceptors);
builder.RegisterType<TImplementation>().AsSelf();
builder.Register(c =>
{
var service = c.Resolve<TImplementation>();
var resolvedInterceptors = ResolveInterceptors<TImplementation, TService>(interceptors, c);
return (TService) _proxyGenerator.CreateInterfaceProxyWithTarget(
typeof(TService),
service,
ProxyGenerationOptions.Default,
resolvedInterceptors
);
}).As<TService>();
}
private static void ValidateInput<TService>(Type[] interceptors)
{
if (!typeof(TService).IsInterface)
throw new ArgumentException("Type must be interface");
if (interceptors.Any(i => i != typeof(IAsyncInterceptor)))
throw new ArgumentException("Only IAsyncInterceptor types are expected");
}
private static IAsyncInterceptor[] ResolveInterceptors<TImplementation, TService>(Type[] interceptors,
IComponentContext c) where TImplementation : class
{
var resolvedInterceptors = new List<IAsyncInterceptor>
{
c.Resolve<LoggingInterceptor>(),
c.Resolve<UnitOfWorkInterceptor>()
}.Concat(interceptors
.Where(i => i != typeof(UnitOfWorkInterceptor)
&& i != typeof(LoggingInterceptor))
.Select(i => (IAsyncInterceptor) c.Resolve(i))).ToArray();
return resolvedInterceptors;
}
}
}
I am using castle core for unit of work and logging hence the name UnitOfWorkInterceptor and LogginInterceptor. Change these two to your desired defaults. Default interceptors must be registered in this way:
public class SomeModule: Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<UnitOfWorkInterceptor>().AsSelf();
builder.RegisterType<LoggingInterceptor>().AsSelf();
builder.RegisterApplicationService<SampleService, ISampleService>();
builder.RegisterType<SampleRepository>().As<ISampleRepository>();
}
}
In the above code snippet I've also demonstrated the usage provided extension method. Doing it this way I get red of tag interfaces and placing extra attributes on interfaces. That way I can keep my ApplicationService interfaces free of framework/3rd party library dependencies.
I hope this helps.

ASP.NET 5 beta8 Custom File Provider with Autofac

We have a default ASP.NET Web Application in which we want to use a custom file provider.
public class CustomFileProvider : IFileProvider
{
public IDirectoryContents GetDirectoryContents(string subpath)
{
...
}
public IFileInfo GetFileInfo(string subpath)
{
...
}
public IChangeToken Watch(string filter)
{
...
}
}
When we configure the provider using the following code it works fine and the custom file provider gets called by the framework.
public void ConfigureServices(IServiceCollection services)
{
// Add MVC services to the services container.
services.AddMvc();
services.Configure<RazorViewEngineOptions>(options =>
{
// replace the fileprovider with the custom file provider
options.FileProvider = new CustomFileProvider();
});
}
When we extend the configuration with a custom di container like Autofac the custom file provider isn't called anymore. This used to work fine in beta5.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add MVC services to the services container.
services.AddMvc();
services.Configure<RazorViewEngineOptions>(options =>
{
// replace the fileprovider with the custom file provider
options.FileProvider = new CustomFileProvider();
});
// Create the Autofac container builder.
var builder = new ContainerBuilder();
// Populate the services.
builder.Populate(services);
// Build the container.
var container = builder.Build();
// Resolve and return the service provider.
return container.Resolve<IServiceProvider>();
}
Did something change on how to use a custom file provider?
There is an existing issue with the Autofac integration that is being worked through that addresses this exact thing.

Asp.net web api with autofac and Hangfire

I recently upgraded to a new version of Hangfire and I am struggeling trying to setup my webapi with autofac and Hangfire. I'm using Autofac Hangfire integration version 1.1 and Hangfire 1.4.2. I'm using Owin to host. I keep getting following error:
The requested service 'IFoo' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
Here is my owin startup configuration. All my registrations are made in the AutofacStandardModule class
public class Startup
{
public void Configuration(IAppBuilder app)
{
//we will have the firewall block all CE endpoints from the outside instead
//ConfigureOAuthTokenConsumption(app);
var storage = new SqlServerStorage("connection string");
JobStorage.Current = storage;
app.UseHangfireServer(new BackgroundJobServerOptions(),storage);
app.UseHangfireDashboard("/Hangfire",new DashboardOptions(),storage);
var builder = new ContainerBuilder();
builder.RegisterModule(new AutofacStandardModule());
var container = builder.Build();
GlobalConfiguration.Configuration.UseAutofacActivator(container);
}
}
Also, here is my web api config class. I dont see how I should be configuring Hangfire here also though..
public static class WebApiConfig
{
public static void Register(HttpConfiguration config, Autofac.Module moduleToAppend)
{
config.MapHttpAttributeRoutes();
config.EnableCors();
config.EnableSystemDiagnosticsTracing();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(
Assembly.GetExecutingAssembly())
.Where(t =>
!t.IsAbstract && typeof(ApiController).IsAssignableFrom(t))
.InstancePerLifetimeScope();
builder.RegisterModule(
new AutofacStandardModule());
if (moduleToAppend != null)
{
builder.RegisterModule(moduleToAppend);
}
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(
container);
//Hangfire.GlobalConfiguration.Configuration.UseAutofacActivator(container);
//JobActivator.Current = new AutofacJobActivator(container);
}
}
I solved the issue, it seemed I hadn't specified clearly enough which type my job was when enqueuing.
What is did was to change
_jobClient.Enqueue(
() => _foo.Bar(fooId, fooId2));
..into..
_jobClient.Enqueue<IFoo>(x => x.Bar(fooId, fooId2));

SignalR issues with SignalR.Ninject and overiding the IConnectionIdFactory

I have all the Nuget Bits for SignalR , I am trying to use my own clientIDs as well as the dependency Injection container that comes with SignalR for all my other repositories and such. Now the strange thing is this jQuery to connect to the hub fails on:
debugger;
// Proxy created on the fly
var chat = $.connection.chat;
Basically, the chat object becomes undefined as if SignalR cannot be resolved. This started happening once I tried to overide the default resolver for SignalR with the code below.
What am I missing here?
Another issue I am having is I am not sure if my UserClientIDfactory which implements IConnectionIdFactory
is working either.
Here is the MVC3 code in my Global.asax
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
RegisterServices(kernel);
return kernel;
}
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<UserIdClientIdFactory>()
.To<UserIdClientIdFactory>()
.InRequestScope();
//Rest of the other stuff to inject
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//ninject calls to create the kernal etc
IKernel kernel = CreateKernel();
//TO DO using signal IR resolver
var resolver = new NinjectDependencyResolver(kernel);
SignalR.Hosting.AspNet.AspNetHost.SetResolver(resolver);
}
Finally, here is the code for my custom clientIDfactory
public class UserIdClientIdFactory : IConnectionIdFactory
{
#region IConnectionIdFactory Members
string IConnectionIdFactory.CreateConnectionId(SignalR.Hosting.IRequest request)
{
// get and return the UserId here, in my app it is stored
// in a custom IIdentity object, but you get the idea
return HttpContext.Current.User.Identity.Name != null ?
//TO DO change to get profileID from Appfabric or the database and log user infor
HttpContext.Current.User.Identity.Name.ToString() :
Guid.NewGuid().ToString();
}
#endregion
}
As I read your question you ask how to do proper dependency injection in ASP.NET MVC and SignalR using the same DI container (and hence only need to declare bindings in one place). If this is correct understood, I once wrote a blog post regarding this: http://lcdev.dk/2012/02/14/using-signalr-ninject-with-asp-net-mvc3-and-the-ninject-mvc3-nuget-package/
In the blog post I assume that you are using ASP.NET MVC3 as well as the Ninject.MVC3 and the SignalR.Ninject Nuget packages.
However, if this is not the case I do have a comment to your code. To me it seems like that the kernel used to make your bindings (in RegisterServices) is not the kernel you actually register with SignalR. And if this is the case, then of course SignalR won't know about your intended bindings and might throw an exception as result of your use of an un-instantiated object reference -> which then might explain why you no longer can connect to your SignalR hub.
ok thanks for the your post man, made me do some more digging , I read the rest of the post you linked about how to use Ninject with MVC3 which lead me to realize that I had ninject but not the Nuget Bits for Ninject Mvc3 , I added that and alos modifed my global.asax using the following post
http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/
here is the working code in gloabal.asax I also removed the bootstrapper that NinJect mvc3 added to the application start folder since that is how it works in the above post
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.IgnoreRoute("{*allaxd}", new { allaxd = #".*\.axd(/.*)?" }); //added for mango chat
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
public override void Init()
{
this.AuthenticateRequest += new EventHandler(MvcApplication_AuthenticateRequest);
this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
#region "Ninject stuff for dependancy Injection
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
// kernel.Load(Assembly.GetExecutingAssembly());
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<UserIdClientIdFactory>()
.To<UserIdClientIdFactory>()
.InRequestScope();
SignalR.Hosting.AspNet.AspNetHost.DependencyResolver.Register(typeof(IConnectionIdFactory), () => new UserIdClientIdFactory());
}
#endregion
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
//for project awesome
ModelMetadataProviders.Current = new AwesomeModelMetadataProvider();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}