2
votes

Having a hard time trying to setup AutoFac with some async non httprequest.

I have the following on App_Start

        var builder = new ContainerBuilder();
        builder.RegisterControllers(typeof(MvcApplication).Assembly);
        builder.RegisterType<sfEntities>().As<IUnitOfWork>().InstancePerHttpRequest();
        builder.RegisterGeneric(typeof(sfRepository<>)).As(typeof(IRepository<>)).InstancePerHttpRequest();
        builder.RegisterGeneric(typeof(BaseServices<>)).As(typeof(IBaseServices<>)).InstancePerHttpRequest();
        builder.RegisterType<EmailServices>().As<IEmailServices>().InstancePerHttpRequest();
        builder.RegisterType<UserServices>().As<IUserServices>().InstancePerHttpRequest();
        builder.RegisterType<ChatServices>().As<IChatServices>().InstancePerHttpRequest();
        builder.RegisterType<DefaultFormsAuthentication>();
        builder.RegisterType<WebSecurity>();
        builder.RegisterType<Chat>();
        IContainer container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

If I change to InstancePerLifetimeScope() I get problems with UnitofWork.SaveChanges(). Setup this way works fine except for async calls.

p.s.: UnitOfWork pass the EF DbContext between services to ensure that the same instance is used and to dispose properly. If I change to InstancePerLifetimeScope I was getting identity conflicts when calling .SaveChanges(), probably because there should be more than one instance of UnitOfWork.

The following code throws the following exception:

Timer timer = new Timer(new TimerCallback(OnTimer), null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));

    private static void OnTimer(object o)
    {
         using (var timerScope = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
         {
             var chatServices = timerScope.Resolve<IChatServices>();
             chatServices.MarkInactiveUsers();
         }
    }

No scope with a Tag matching 'httpRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being reqested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.

On SignalR, the following code throws the following exception:

SignalR.GlobalHost.DependencyResolver.Register(typeof(Chat), () => new Chat(DependencyResolver.Current.GetService<IUnitOfWork>(), DependencyResolver.Current.GetService<IChatServices>(), DependencyResolver.Current.GetService<IUserServices>()));

The request lifetime scope cannot be created because the HttpContext is not available

Thanks in advance!

2

2 Answers

3
votes

Having a hard time trying to setup AutoFac with some async non httprequest.

For non-http requests, or more specifically, for non-ASP.NET pipeline requests (like WCF or ServiceStack), you should definitely change all InstancePerHttpRequest() code to InstancePerLifetimeScope(). You can and should do this because InstancePerLifetimeScope() will make it resolvable in both ASP.NET pipeline and non-ASP.NET pipeline contexts.

If I change to InstancePerLifetimeScope() I get problems with UnitofWork.SaveChanges(). Setup this way works fine except for async calls... If I change to InstancePerLifetimeScope I was getting identity conflicts when calling .SaveChanges(), probably because there should be more than one instance of UnitOfWork.

Yes, there should be more than one instance of UnitOfWork, but you can achieve that with a single registration that should be scoped to InstancePerLifetimeScope():

Example:

builder.RegisterType<NhUnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
1
votes

The IChatServices service is registered as InstancePerHttpRequest and will therefore only be available within the http request lifetime scope. You are resolving from the application scope which have no "access" to the current request and therefore fail with the error you mention. So yes, to get the timer to work you must register the service in the application scope.

Basically, you can have request scoped services that access application scoped services, but not the other way around.

Question is: what is UnitOfWork.SaveChanges do and what "problems" do you get? Please elaborate.