Add SignalR's ITransportHeartbeat to Autofac - c#

I'm trying to use Autofac to have one instance of the ITransportHeartbeat interface for my ASP.NET MVC 5 app to track all connected users. I use the ITransportHeartbeat interface to determine if a user's connection is still active. To do this, I need to have one instance of the SignalR hub for the app by using Autofac.
The challenge: when I run the app, it never hits the overridden OnConnected(), OnReconnected(), or OnDisconnected() in the SignalR hub. However, the client's hub.start() is hit and I see the Hub Started message when the page loads:
$.connection.hub.start().done(function () {
console.log("Hub Started");
});
If I add a parameterless constructor to the hub, it does hit them, but then the ITransportHeartbeat interface is null and that defeats the point of injecting the interface into the hub.
I've referenced the following for help:
https://stackoverflow.com/a/49214891/177416
https://autofaccn.readthedocs.io/en/latest/integration/signalr.html
https://stackoverflow.com/a/36476106/177416
https://stackoverflow.com/a/21126852/177416
Here is Startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.InstancePerRequest();
var signalRConfig = new HubConfiguration();
builder.RegisterType<MonitoringHubBase>().ExternallyOwned();
builder.RegisterType<Autofac.Integration.SignalR.AutofacDependencyResolver>()
.As<Microsoft.AspNet.SignalR.IDependencyResolver>()
.SingleInstance();
builder.Register(context =>
context.Resolve<Microsoft.AspNet.SignalR.IDependencyResolver>()
.Resolve<IConnectionManager>()
.GetHubContext<MonitoringHubBase, IMonitoringHubBase>())
.ExternallyOwned();
builder.RegisterType<Microsoft.AspNet.SignalR.Transports.TransportHeartbeat>()
.As<Microsoft.AspNet.SignalR.Transports.ITransportHeartbeat>()
.SingleInstance();
var container = builder.Build();
DependencyResolver.SetResolver(
new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
signalRConfig.Resolver = container
.Resolve<Microsoft.AspNet.SignalR.IDependencyResolver>();
app.UseAutofacMiddleware(container);
app.MapSignalR("/signalr", signalRConfig);
config.DependencyResolver =
new AutofacWebApiDependencyResolver((IContainer)container);
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
Here is the hub from which the other hubs derive:
public interface IMonitoringHubBase
{
Task OnConnected();
Task OnReconnected();
Task OnDisconnected(bool stopCalled);
}
public abstract class MonitoringHubBase : Hub<IMonitoringHubBase>
{
private ITransportHeartbeat Heartbeat { get; }
public MonitoringHubBase(ITransportHeartbeat heartbeat)
{
Heartbeat = heartbeat;
}
public MonitoringHubBase()
{
}
public override async Task OnConnected()
{
// Add connection to db...
}
public override async Task OnReconnected()
{
// Ensure connection is on db...
}
public override async Task OnDisconnected(bool stopCalled)
{
// Remove connection from db...
}
public async Task SomeMethod(int id)
{
// Heartbeat object used here...
}
}
And here's one of the hubs that inherits from the base hub:
[HubName("MyHub")]
public class MyHub : MonitoringHubBase
{
public MyHub(ITransportHeartbeat heartbeat) : base(heartbeat)
{
}
public MyHub()
{
}
}

Found a solution! Removed the parameterless c'tor from the hub and modified Startup.cs to this; hope this helps the next person struggling with this:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
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<Microsoft.AspNet.SignalR.Transports.TransportHeartbeat>()
.As<Microsoft.AspNet.SignalR.Transports.ITransportHeartbeat>()
.SingleInstance();
builder.RegisterType<Autofac.Integration.SignalR.AutofacDependencyResolver>()
.As<Microsoft.AspNet.SignalR.IDependencyResolver>()
.SingleInstance();
var container = builder.Build();
var signalRConfig = new HubConfiguration();
signalRConfig.Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container);
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
app.Map("/signalr", map =>
{
map.UseAutofacMiddleware(container);
var hubConfiguration = new HubConfiguration
{
Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container),
};
map.RunSignalR(hubConfiguration);
});
config.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)container);
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
With help from this article: https://kwilson.io/blog/get-your-web-api-playing-nicely-with-signalr-on-owin-with-autofac/

Related

How to use or resolve a service(or dependency) in the Startup class

When using IdentityServerBearerTokenAuthentication middle-ware, I would like to have access to a service that is registered in my WebApi config. Specifically, I want to call a service to give me the default user id and add it to the Claims in the the request. See my TODO in the example code below in the OnValidateIdentity lambda.
//startup.cs
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["Federation.IdentityServerPath"]
});
// azure functions will authenticate using Azure AD Tokens
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
Provider = new OAuthBearerAuthenticationProvider()
{
OnValidateIdentity = context =>
{
//add the account id claim if it's specified in the header
var accountIdString = context.Request.Headers["X-AccountId"];
if (!string.IsNullOrWhiteSpace(accountIdString) && Guid.TryParse(accountIdString, out var _))
{
context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, accountIdString));
}
else
{
//TODO: Need to pull the system user or admin user and jam that account id into NameIdentifier.
var systemDefaultId = "";
// How do I get a dependency from HttpContext or context.OwinContext?????
context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, systemDefaultId));
}
context.Ticket.Identity.AddClaim(new Claim("cl_aad_user", "true"));
return Task.FromResult<object>(null);
}
}
}
);
}
}
}
The application services are registered with the autofac builder pattern.
e.g.
builder.RegisterType<AccountInfoService>().As<IAccountInfoService>().InstancePerRequest(); in a static method that is called in a Global class (Global.asax).
// Global.asax.cs
...
private void Application_Start(object sender, EventArgs e)
{
// ... truncated code
// registers the services
GlobalConfiguration.Configure(WebApiConfig.Register);
}
I have tried grabbing a service from the HttpContext, but it always resolves to null. (i.e. var db = (IOutcomesContext)HttpContext.Current.Request.RequestContext.HttpContext.GetService(typeof(IAccountService));)
I have also checked this answer, but I am not using that middle ware.
This is how I register the dependencies.
// WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
RegisterIoc(config);
}
private static void RegisterIoc(HttpConfiguration config)
{
ContainerBuilder builder = GetIocContainerBuilder();
builder.RegisterWebApiFilterProvider(config);
//all the external dependencies
builder.RegisterType<InstitutionRequestContext>().As<IInstitutionRequestContext>().InstancePerRequest();
Autofac.IContainer container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
internal static ContainerBuilder GetIocContainerBuilder()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// ... truncated service list
builder.RegisterType<AccountInfoService>().As<IAccountInfoService>().InstancePerRequest();
return builder;
}
}
Thanks for the help.
I ended up solving the above problem by switching the application to use the app.UseAutofacMiddleware(container); middle ware as described in this answer.
For my particular use case, it only involved a few changes, specifically...
removing the autofac from the Global config file (e.g. Global.asax)
updating my WebApiConfig to return the Autofac.IContainer
adding 3 or so method calls to the Startup.Configure class as described in their documentation

How to use Dependency Injection (AutoFac) with Repository Pattern in C#

Everyone
I am creating a Project using the repository pattern. I am stuck while implementing Dependency Injection using Autofac Library, Please help me How to implement it in Solution.
I have created a console Library Project where I registered all my component like below
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<TestService>().As<ITestService>().InstancePerLifetimeScope();
base.Load(builder);
}
}
But My Question How to tell MVC Project that I have registered my components,
Do I need to call in Global.asax file or there is other best way to do it.
I didn't find any solution that helps me to implement it
Please help me out to implement it.
Github Repository Link - https://github.com/Harshk16/WebApi.
Thank you
You can
Create a Bootstrapper.cs file under the Start_App folder and paste the following code.
Just replace the YOUR_REPOSITORY and YOUR_IREPOSITORY for your implementations.
public static class Bootstrapper
{
public static void Run()
{
SetAutofacContainer();
}
private static void SetAutofacContainer()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
// Repositories
builder.RegisterType<YOUR_REPOSITORY>().As<YOUR_IREPOSITORY>().InstancePerRequest();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
In the global.asax
protected void Application_Start()
{
// standard code
//....
// Autofac and Automapper configurations
Bootstrapper.Run();
}
}
I used Dapper in example.
Edit: Make sure you are using appropriate AutoFac MVC Integration DLL in your project.
Repository Configuration
public interface IOrmRepository
{
IDbConnection Connection { get; }
}
public class DapperRepository : IOrmRepository
{
private readonly IDbConnection _context;
public DapperRepository(IDbConnection context)
{
_context = context;
}
public IDbConnection Connection
{
get { return _context; }
}
}
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
this.RegisterDependencies(); // these are my dependencies
}
private void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.Register(ctx =>
{
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"];
if (string.IsNullOrWhiteSpace(connectionString.ConnectionString))
throw new InvalidOperationException("No ConnectionString");
return new System.Data.SqlClient.SqlConnection(connectionString.ConnectionString);
}).As<System.Data.IDbConnection>().InstancePerLifetimeScope();
builder.RegisterType<DapperRepository>().As<IOrmRepository>().InstancePerLifetimeScope();
// Other dependencies is here
builder.RegisterModelBinders(typeof(MvcApplication).Assembly);
builder.RegisterModelBinderProvider();
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterSource(new ViewRegistrationSource());
builder.RegisterFilterProvider();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
You need to set the container as below in MCV application.
Refer the link https://autofaccn.readthedocs.io/en/latest/integration/mvc.html# for detailed information.
public static class IocConfigurator
{
public static void ConfigureDependencyInjection()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<Repository<Student>>().As<IRepository<Student>>();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}

Migration of BotState to Azure Table Storage: no more possibility to access UserData without IDialogContext?

I recently switched my custom state data from the now deprecated StateClient to Azure Table Storage using Microsoft documentation, following the steps:
Created a storage
Added to connection strings
Added to Autofac registration in Global.asax.cs
This is working well for all call like context.UserData.SetValue("myKey", "myValue"); inside dialogs.
But it seems that there is no more possibility to get UserData directly from an Activity object when we have no IDialogContext, for example if you want to use these data from your MessageController.
Previously, I was doing:
var botState = activity.GetStateClient().BotState;
var userData = await botState.GetUserDataAsync(activity.ChannelId, activity.From.Id, token);
These actions are announced as deprecated in latest BotBuilder release. Any solution to do the equivalent?
EDIT:
Adding my global.asax.cs:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new ExceptionFilter());
var builder = new ContainerBuilder();
//register the bot builder module
builder.RegisterModule(new DialogModule());
//register project dependencies
builder.RegisterModule(new BotModule());
//Http config
var config = GlobalConfiguration.Configuration;
//register controller
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
//create container
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
And the BotModule associated:
public class BotModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
// Scorable pour l'interruption de dialogue
builder.Register(c => new InterruptScorable(c.Resolve<IDialogTask>())).As<IScorable<IActivity, double>>().InstancePerLifetimeScope();
// Manage BotData in Sql Datatable
var store = new TableBotDataStore(ConfigurationHelper.XXXXX, "BotData");
builder.Register(c => store).Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore).AsSelf().SingleInstance();
builder.Register(c => new CachingBotDataStore(store, CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency)).As<IBotDataStore<BotData>>().AsSelf().InstancePerLifetimeScope();
// Logger de conversation
builder.Register(c => new ActivityLogger(c.Resolve<IBotData>())).As<IActivityLogger>().InstancePerLifetimeScope();
// Dialogue de base
builder.RegisterType<RootDialog>().As<IDialog<object>>().InstancePerDependency();
}
}
I've modified your code a little, to remove the deprecated .Build() method.
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
Conversation.UpdateContainer(builder =>
{
builder.RegisterModule(new DialogModule());
builder.RegisterModule<BotModule>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
});
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(Conversation.Container);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
After registering the modules this way, the code Jason Sowers shared on this issue should help: How can I access Bot Framework ConversationData outside of a dialog?

HangFire Recurring Jobs Fail on server with Autofac.Core.Registration.ComponentNotRegisteredException or No parameterless exception

I've just started using HangFire. The jobs seem to work fine when I run it locally from IIS Express, but when I run it on my dev server I get errors that no parameterless constructor defined. I was using Simple Injector earlier but now I've moved to Autofac.
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ConfigureIoc(app);
ConfigureRoutes();
ConfigureHangfire(app);
ScheduleXeroMonitor();
}
public void ConfigureHangfire(IAppBuilder app)
{
var authList = new List<IDashboardAuthorizationFilter>
{
new ClaimsBasedAuthorizationFilter("role", "admin")
};
var options = new DashboardOptions
{
Authorization = authList,
AppPath = "/Manage"
};
app.UseHangfireDashboard("/hangfire", options);
app.UseHangfireServer();
}
public void ConfigureRoutes()
{
BundleTable.EnableOptimizations = true;
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
public void ConfigureIoc(IAppBuilder app)
{
var builder = DependencyRegistrar.RegisterClasses();
var container = builder.Build();
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
GlobalConfiguration.Configuration.UseAutofacActivator(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
CookieSecure = CookieSecureOption.SameAsRequest,
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AuthenticationMode = AuthenticationMode.Passive
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
}
public void ScheduleXeroMonitor()
{
var backGroundWorker = DependencyResolver.Current.GetService<IXeroBackGroundWorker>();
RecurringJob.AddOrUpdate("XeroPolling", () => backGroundWorker.PollJobs(), Cron.Minutely); //change the Cron to every 5 minutes
var emailWorker = DependencyResolver.Current.GetService<IEmailService>();
RecurringJob.AddOrUpdate("EmailPolling", () => emailWorker.SendQueuedEmails(), Cron.Minutely);//"*/10 * * * *"
}
}
Here's the code where I register my dependencies.
public class DependencyRegistrar
{
public static ContainerBuilder RegisterClasses()
{
var builder = new ContainerBuilder();
builder.RegisterType<XeroSyncRestClient>().As<IXeroSyncRestClient>().InstancePerLifetimeScope();
builder.RegisterType<XeroBackGroundWorker>().As<IXeroBackGroundWorker>().InstancePerLifetimeScope();
builder.RegisterType<EmailService>().As<IEmailService>().InstancePerLifetimeScope();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
return builder;
}
}
Could someone please tell me what I'm doing wrong? I've also posted this on Hangfire discussion forum
Since you already added AutoFac in GlobalConfiguration.Configuration.UseAutofacActivator(container); You just simple let hangfire and Autofac resolve dependencies for you.
public void ScheduleXeroMonitor()
{
//var backGroundWorker = DependencyResolver.Current.GetService<IXeroBackGroundWorker>(); --> let hangfire and autofac do this for you
RecurringJob.AddOrUpdate<IXeroBackGroundWorker>("XeroPolling", backGroundWorker => backGroundWorker.PollJobs(), Cron.Minutely); //change the Cron to every 5 minutes
//var emailWorker = DependencyResolver.Current.GetService<IEmailService>();--> let hangfire and autofac do this for you
RecurringJob.AddOrUpdate<IEmailService>("EmailPolling", emailWorker => emailWorker.SendQueuedEmails(), Cron.Minutely);//"*/10 * * * *"
}

Using Autofac with Web Api 2 and Owin

Im new to DI libraries and trying to use Autofac in a WebApi 2 project with Owin. This is my Owin Startup class,
[assembly: OwinStartup(typeof(FMIS.SIGMA.WebApi.Startup))]
namespace FMIS.SIGMA.WebApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
WebApiConfig.Register(config);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
ConfigureOAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
When I call a Api method I get this error
An error occurred when trying to create a controller of type
'MyController'. Make sure that the controller has a
parameterless public constructor.
What am I missing here?
MyController code is something like this
public class MyController : ApiController
{
ISomeCommandHandler someCommanHandler;
public MyController(ISomeCommandHandler SomeCommandHandler)
{
this.someCommanHandler = SomeCommandHandler;
}
// POST: api/My
public void Post([FromBody]string value)
{
someCommanHandler.Execute(new MyCommand() {
Name = "some value"
});
}
// GET: api/My
public IEnumerable<string> Get()
{
}
// GET: api/My/5
public string Get(int id)
{
}
}
You have set DependencyResolver to AutofacWebApiDependencyResolver, so Autofac comes into play and instantiates dependencies for you. Now you have to explicitly tell Autofac which concrete implementations should be used when an instance of interface is required.
Your controller requires an instance of ISomeCommandHandler:
MyController(ISomeCommandHandler SomeCommandHandler)
So you need to configure types that expose that interface:
builder.RegisterType<CommandHandler>.As<ISomeCommandHandler>();
Have a look at this documentation section for more examples about Autofac registration concepts.

Categories

Resources