autofac with signalr no parameterless constructor defined for this object - c#

I'm using autofac on my current Asp project and everything works fine until i decided to use dependancy injection in a signalR Hub
here's my startup class
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
//builder.RegisterHubs(Assembly.GetExecutingAssembly());
builder.RegisterType<DiscussionHub>();
// Repositories
builder.RegisterAssemblyTypes(typeof(LanguagesRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerRequest();
// Services
builder.RegisterAssemblyTypes(typeof(LanguageService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
var config = new HubConfiguration
{
Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container)
};
app.UseAutofacMiddleware(container);
AutoMapperConfiguration.Configure();
app.MapSignalR("/signalr",config);
}
}
and here's my Hub
public class DiscussionHub : Hub
{
private readonly IDiscussionService _discussionService;
public DiscussionHub(IDiscussionService discussionService)
{
_discussionService = discussionService;
}}
the error is that i'm getting no parameterless constructor on my Hub? any suggestion ?!

You should register your hub ExternallyOwned it should manage lifetimescope by itself. That's mean autofac will not disposed them.
Second, everything will be resolved from root container in your hub. That's mean Per Dependency or Per LifeTimeScope will live with your hub(forever with app). So you should manage lifetime in your hub.
Even if we manage life time in your hub, Per Request will not be supported. Because of this, when we create new lifetimescope, we will create it with AutofacWebRequest tag. That way, we can resolve your Per Request instance. But pay attention this instance will be totaly different with other instance in normal request lifetimescope.
Your Hub should be like this:
public class DiscussionHub : Hub
{
private readonly ILifetimeScope _hubLifetimeScope;
private readonly IDiscussionService _discussionService;
public MyHub(ILifetimeScope lifetimeScope)
{
// Create a lifetime scope for the hub.
_hubLifetimeScope = lifetimeScope.BeginLifetimeScope("AutofacWebRequest");
// Resolve dependencies from the hub lifetime scope.
_discussionService = _hubLifetimeScope.Resolve<IDiscussionService>();
}
protected override void Dispose(bool disposing)
{
// Dipose the hub lifetime scope when the hub is disposed.
if (disposing && _hubLifetimeScope != null)
{
_hubLifetimeScope.Dispose();
}
base.Dispose(disposing);
}
}
Your register should be like this:
.
.
builder.RegisterType<DiscussionHub>().ExternallyOwned();
var container = builder.Build();
GlobalHost.DependencyResolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container);
.
.
Owin Integration:
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// STANDARD SIGNALR SETUP:
// Get your HubConfiguration. In OWIN, you'll create one
// rather than using GlobalHost.
var config = new HubConfiguration();
// Register your SignalR hubs.
builder.RegisterHubs(Assembly.GetExecutingAssembly());
// Set the dependency resolver to be Autofac.
var container = builder.Build();
config.Resolver = new AutofacDependencyResolver(container);
// OWIN SIGNALR SETUP:
// Register the Autofac middleware FIRST, then the standard SignalR middleware.
app.UseAutofacMiddleware(container);
app.MapSignalR("/signalr", config);
}
Check more detail.

Related

Inject SignalR IHubContext into controller with Autofac

I'm trying to inject a SignalR IHubContext into a Web API 2.x controller in an ASP.NET MVC 5 app Framework 4.72 (not .NET Core). It's throwing this exception when calling the Web API controller MyController:
An error occurred when trying to create a controller of type 'MyController'. Make sure that the controller has a parameterless public constructor
The inner exception says:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyController' can be invoked with the available services and parameters: Cannot resolve parameter 'Microsoft.AspNet.SignalR.IHubContext[MyHub] context' of constructor 'Void .ctor(Microsoft.AspNet.SignalR.IHubContext [MyHub])'.
I don't mind doing this using property injection but haven't had any luck getting that to work. So I'm doing injection into the c'tor of the controller.
I've followed these answers for help:
https://stackoverflow.com/a/37913821/177416 --> c'tor injection
https://stackoverflow.com/a/29793864/177416 --> c'tor injection
https://stackoverflow.com/a/26810399/177416 --> property injection
https://stackoverflow.com/a/15600493/177416 --> property injection
Here's the Web API controller:
public class MyController : WebApiController
{
public IHubContext<MyHub> Context { get; set; }
public MyController(IHubContext<MyHub> context)
{
Context = context;
}
}
And here's the pertinent part of the Startup.cs:
public void Configuration(IAppBuilder app)
{
// Other code...
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
builder.RegisterHubs(Assembly.GetExecutingAssembly());
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.InstancePerRequest();
builder.RegisterType<AutofacDependencyResolver>()
.As<IDependencyResolver>()
.SingleInstance();
builder
.Register(c => c.Resolve<IConnectionManager>().GetHubContext<MyHub>())
.Named<IHubContext>("MyHub");
builder.RegisterType<MyController>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IHubContext),
(pi, ctx) => ctx.ResolveNamed<IHubContext>("MyHub")
)
);
var container = builder.Build();
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)container);
app.Map("/signalr", map =>
{
var hubConfiguration = new HubConfiguration
{
Resolver = new AutofacDependencyResolver(container),
};
map.RunSignalR(hubConfiguration);
});
}
What am I missing? Thanks.
Your first problem is that typeof(IHubContext) is not the same as typeof(IHubContext<MyHub>). You can get around that by using:
pi.ParameterType == typeof(IHubContext).MakeGenericType(typeof(MyHub))
However, old versions of SignalR don't support the generic interfaces very well, so it would probably work better if you left the comparison as is, and inject an IHubContext rather than an IHubContext<MyHub> in MyController.

How to use AutoFac in Web API to resolve a service at runtime?

I have an API (eg: ItemController.cs) which would obtain the Authorization Token from the Request Header at run time. With the Token, then only I pass into my Service Class (eg: ServiceItem.cs).
Here's how I did.
At the Startup.cs, I register my ServiceItem
var builder = new ContainerBuilder();
builder.RegisterType<ServiceItem>();
container = builder.Build(); //Note that, my container is a static variable
In my API, I resolve it in this way:
[Authorize]
[Route("GetData")]
[HttpGet]
public IHttpActionResult GetData([FromUri] Filter filter)
{
using (var scope = Startup.container.BeginLifetimeScope())
{
var serviceItem = Startup.container.Resolve<ServiceItem>(
new NamedParameter("token", Request.GetHeader("Authorization"))
);
return Ok(serviceItem.getItem(filter)); //filter is a param from webAPI
}
}
Question:
Is this how the Autofac normally work in web API? First, i am using a global static IContainer. Second, the codes look repetitive if i expose a few more functions.
I was thinking to resolve the ServiceItem in the constructor of the API. But the authorization token is not available yet.
Any suggestion is appreciated.
P.S.:
Here's my ServiceItem which, in the constructor, has a param 'token'
public class ServiceItem
{
public string token;
public ServiceItem(string token)
{
this.token = token;
}
public void doSomething()
{
//based on token, do processing
}
}
It is a bad idea to refer to a static container within your startup class. That way, you introduce tight coupling between the controller and the startup. Your controller dependencies should be satisfied by constructor parameters. Take at http://docs.autofac.org/en/v4.0.0/integration/aspnetcore.html
The Startup.ConfigureServices method can optionally return a IServiceProvider instance, which allows you to plug-in Autofac into the ASP.NET Core Dependency Injection framework:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var builder = new ContainerBuilder();
builder.RegisterType<MyType>().As<IMyType>();
builder.Populate(services);
this.ApplicationContainer = builder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}
After initializing your container, constructor parameters will be automatically resolved by Autofac:
public class MyController
{
private readonly IMyType theType;
public MyController(IMyType theType)
{
this.theType = theType;
}
....
}

ASP.NET Core 2.0 inject Controller with Autofac

I'm trying to inject my controller with Autofac. Unfortunately I am unable to configure Autofac in away so that the 'DefaultControllerActivator` wont construct my controllers?
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<ServiceModule>();
containerBuilder.Populate(services);
containerBuilder.RegisterType<LoginController>().PropertiesAutowired();
ApplicationContainer = containerBuilder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterModule(new DataProviderModule());
builder.RegisterType(typeof(LoginService)).As(typeof(ILoginService)).InstancePerRequest();
}
}
[Route("api/[controller]")]
public class LoginController : Controller
{
private readonly ILoginService _loginService;
public LoginController(ILoginService loginService)
{
_loginService = loginService;
}
}
I followed the documentation of Autofac as shown above. Unfortunately the LoginController will not be constructed because it requires an injection.
edit: If there is a way of using "Modules" without Autofac, I'd be very interesting for any suggestions :)
Thanks you in advance!
By default, ASP.NET Core will resolve the controller parameters from the container but doesn’t actually resolve the controller from the container. This usually isn’t an issue but it does mean:
The lifecycle of the controller is handled by the framework, not the request lifetime.
The lifecycle of controller constructor parameters is handled by the request lifetime.
Special wiring that you may have done during registration of the controller (like setting up property injection) won’t work.
You can change this by specifying AddControllersAsServices() when you register MVC with the service collection. Doing that will automatically register controller types into the IServiceCollection when you call builder.Populate(services).
public class Startup
{
public IContainer ApplicationContainer {get; private set;}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add controllers as services so they'll be resolved.
services.AddMvc().AddControllersAsServices();
var builder = new ContainerBuilder();
// When you do service population, it will include your controller
// types automatically.
builder.Populate(services);
// If you want to set up a controller for, say, property injection
// you can override the controller registration after populating services.
builder.RegisterType<MyController>().PropertiesAutowired();
this.ApplicationContainer = builder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}
}
Use InstancePerLifetimeScope in ASP.NET Core. The differences between ASP.NET and ASP.NET Core like this are documented.

Unity Dependency Injection only working when injecting into MVC Controller?

I wish to inject a service into a custom class i have created> i have been using Dependency injection to inject the same service into my WebApi controllers but cannot seem to understand why it doesn't work in my "InputDataValidationModel" class
This is what i am trying to do:
public class InputDataValidationModel
{
private ISec300_EE_SubmissionRepository _service { get; set; }
public InputDataValidationModel(ISec300_EE_SubmissionRepository service)
{
_service = service;
}
}
In the Global.asax i registered the types as follow:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureApi(GlobalConfiguration.Configuration);
}
void ConfigureApi(HttpConfiguration config)
{
var unity = new UnityContainer();
// Register the Controllers that should be injectable
unity.RegisterType<SEC300_EE_SubmissionController>();
unity.RegisterType<InputDataValidationModel>();
unity.RegisterType<ISec300_EE_SubmissionRepository, Sec300_EE_SubmissionRepository>(new ContainerControlledLifetimeManager());
unity.RegisterType<IClientRepository, ClientRepository>(new ContainerControlledLifetimeManager());
// Finally, override the default dependency resolver with Unity
config.DependencyResolver = new IoCContainer(unity);
}
What I cant understand specifically is that this works perfectly when done in my controller below:
public class SEC300_EE_SubmissionController : ApiController
{
private ISec300_EE_SubmissionRepository _service;
public SEC300_EE_SubmissionController(ISec300_EE_SubmissionRepository service)
{
if (service == null)
{
throw new ArgumentNullException("service");
}
_service = service;
}
}
It does not work because controllers are instantiated by Web API, and when Web API creates a controller it calls BeginScope which is the entry point to call your IoC and proceeds to resolve and constructs objects for you.
In your case InputDataValidationModel is not used and may be for this reason you don't have the instance
update
Do you perhaps have any suggestions on how i would inject my service into InputDataValidationModel ?
Option 1
You can use IDependencyResolver take a look at this link Dependency Resolution with the Unity Container`'
And after this you can resolve your service like this
var instance =GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof (InputDataValidationModel));
Option 2
You might use service locator even it is an anti-pattern
static void Main(string[] args)
{
UnityServiceLocator locator = new UnityServiceLocator(ConfigureUnityContainer(
ServiceLocator.SetLocatorProvider(() => locator);
var a = ServiceLocator.Current.GetInstance<IFoo>();
var b = ServiceLocator.Current.GetInstance<IFoo>();
Console.WriteLine(a.Equals(b));
}
private static IUnityContainer ConfigureUnityContainer()
{
UnityContainer container = new UnityContainer();
container.RegisterType<IFoo, Foo>(new ContainerControlledLifetimeManager());
return container;
}
Hope this help

Hangfire With Autofac in WebApi

I have following configuration in startup.cs but I am getting error although I have installed Hangifre.Autofac nuget package and configured.
No scope with a Tag matching 'AutofacWebRequest' is visible from the
scope in which the instance was requested. This generally indicates
that a component registered as per-HTTP request is being requested 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.
Startup.cs
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
//if (AppConfigHelper.PlatformEnvironment == PlatformEnvironment.LocalHost)
builder.RegisterType<NLogLogger>().As<ILogger>().InstancePerLifetimeScope();
//else
//builder.RegisterType<SentryLogger>().As<ILogger>().InstancePerLifetimeScope();
//builder.RegisterWebApiFilterProvider(configuration);
// REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
//These lines warm up dlls and load into memory for automatic regisration
var r = new ReplyRepository(null);
var s = new BankService();
builder.RegisterModule(new SelfRegisterModule());
builder.RegisterModule(new RepositoryModule());
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EFModule());
builder
.RegisterType<ApplicationOAuthProvider>()
.As<IOAuthAuthorizationServerProvider>()
.PropertiesAutowired() // to automatically resolve IUserService
.SingleInstance(); // you only need one instance of this provider
builder.RegisterType<SellutionUserStore>().As<IUserStore<ApplicationUser, int>>().InstancePerBackgroundJob().InstancePerRequest();
builder.RegisterType<SellutionUserManager>().AsSelf().InstancePerBackgroundJob().InstancePerRequest();
builder.RegisterType<SellutionRoleManager>().AsSelf().InstancePerBackgroundJob().InstancePerRequest();
builder.RegisterType<SellutionSignInManager>().AsSelf().InstancePerBackgroundJob().InstancePerRequest();
builder.Register<IAuthenticationManager>(c => HttpContext.Current.GetOwinContext().Authentication).InstancePerBackgroundJob().InstancePerRequest();
builder.Register<IDataProtectionProvider>(c => app.GetDataProtectionProvider()).InstancePerBackgroundJob().InstancePerRequest();
builder.RegisterType<TicketDataFormat>().As<ISecureDataFormat<AuthenticationTicket>>();
builder.RegisterType<TicketSerializer>().As<IDataSerializer<AuthenticationTicket>>();
builder.Register(c => new DpapiDataProtectionProvider("Sellution360").Create("ASP.NET Identity")).As<IDataProtector>();
builder.RegisterType<CurrencyRatesJob>().AsSelf().InstancePerBackgroundJob();
// BUILD THE CONTAINER
var container = builder.Build();
Hangfire.GlobalConfiguration.Configuration.UseAutofacActivator(container);
JobActivator.Current = new AutofacJobActivator(container);
// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Set the dependency resolver for Web API.
var webApiResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;
// Set the dependency resolver for MVC.
var mvcResolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(mvcResolver);
// Register the Autofac middleware FIRST, then the Autofac MVC middleware.
app.UseAutofacMiddleware(container);
app.UseAutofacMvc().UseCors(CorsOptions.AllowAll);
app.UseAutofacWebApi(GlobalConfiguration.Configuration).UseCors(CorsOptions.AllowAll); ;
IocManager.Instance.IocContainer = container;
ConfigureAuth(app);
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("DefaultConnection");
app.UseHangfireDashboard();
app.UseHangfireServer();
RecurringJob.AddOrUpdate<CurrencyRatesJob>(j => j.Execute(), Cron.Minutely);
}
CurrencyRatesJob.cs
public class CurrencyRatesJob
{
private readonly ILogger _logger;
private readonly IBusinessTypeService _businessTypeService;
public CurrencyRatesJob(ILogger logger, IBusinessTypeService businessTypeService)
{
_logger = logger;
_businessTypeService = businessTypeService;
}
public void Execute()
{
var types = _businessTypeService.GetBusinessTypes();
_logger.Log("waqar");
}
}
InstancePerBackgroundJob creates Per Macthing Life Time scope with BackgroundJobScope tag. But Per Request instances are resolved in another lifetimescope with Request tag. So when you try resolve Per Request object in BackgroundJobScope life time, it gives error. It says, you can only resolve me in Request lifetime not in root or another. So you should use Per Life Time Scope instead of Per Request.
So these Per Life Time Scope registered objects will get parents lifetimescope. If it's singleton they will be in root. If their parent lifetimescope is request they will live with this request scope. It's same for InstancePerBackgroundJob they will live in BackgroundJobScope life time scope.
And it's good for background objects has another life timescope if they use request lifetime scope your objects can be disposed when request finishes. Also if they are in root scope they will never dispose.

Categories

Resources