How do I host a SignalR hub in a WPF app? - autofac

I want to self-host ASP.Net Core SignalR hub in my WPF (.NET Core 6) application. I use Autofac as my DI container and Caliburn.Micro as my MVVM framework.
Getting the hosted SignalR hub to start on application startup works:
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
host = Host.CreateDefaultBuilder()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder => webBuilder
.UseUrls("http://localhost:5100")
.ConfigureServices(services => services.AddSignalR())
.Configure(app =>
{
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapHub<RemoteControlHub>("/remote"));
}))
.Build();
await host.StartAsync();
}
How do I get a SignalR IHubContext injected into my model classes? What would I need to register in my ConfigureContainer(ContainerBuilder builder) method, so that my application class can do something like this:
public MyViewModel(
ILogger logger,
IHubContext<RemoteControlHub> hub)
{
// do something with hub
}
I can't switch to the Microsoft DI, so this is specifically for Autofac.

Related

Where ConfigureServices is gone in in ASP.NET Core 5 MVC

I created new ASP.NET Core 5 MVC application from a template with individual accounts, there is no Startup class nor ConfigureServices method. Where are they gone? I have just a static Main.
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
}
}
The templates have changed. You can configure all your services using builder.Services . The changes are documented in this article

Can't get .net core MVC to redirect 401 to /Account/Login

When I request a controller action that is [Authorize] decorated instead of being redirected to the login page I receive a 401 error.
This is a .net core mvc app using the identity template running on IIS express.
When i run the app from program.cs the redirect to login works fine.
I've added explicit directions to for the cookie authentication to use the /Account/Login redirect both for configuration and services section, as well as configuring Identity to perform this redirect.
I can't get it to work. Below is my StartUp class, what should I change to make it work in IIS express?:
public class Startup
{
private MapperConfiguration _mapperConfiguration { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
_mapperConfiguration = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfileConfiguration());
});
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>(
option => {
option.Cookies.ApplicationCookie.LoginPath = "/Account/Login";
option.Cookies.ApplicationCookie.AutomaticChallenge = true;
option.Cookies.ApplicationCookie.AutomaticAuthenticate = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddDataProtection();
services.AddMvc();
services.AddSignalR();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.Configure<IISOptions>(options => options.AutomaticAuthentication = true);
services.AddSingleton<IMapper>(sp => _mapperConfiguration.CreateMapper());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ApplicationDbContext context, RoleManager<IdentityRole> roleManager)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseIdentity();
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
//app.UseStatusCodePagesWithReExecute("/Home/Error/{0}");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "MyCookies",
SlidingExpiration = true,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
LoginPath = new PathString("/Account/Login")
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "index",
template: "{controller=Home}/{id?}",
defaults: new { action = "Index" });
});
app.UseSignalR();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
MyDbInit.Init(context, roleManager);
}
}
I had this same problem all night and could not find a solution to it. Running the site directly from Kestrel redirected fine, but through IIS or IIS Express it simply would not redirect - it would go to a white page.
After posting to the Identity Git about it, I realized that my template was set up to run under 1.0.1 of the framework, not 1.1.0. I updated it to use 1.1.0 and updated all the Nuget packages to 1.1.0 and now it is redirecting in IIS and IIS Express properly.
I'm not sure if the package updates "fixed" something that was screwy, or if this was simply a problem with 1.0.1 that was fixed in 1.1.0.
https://blogs.msdn.microsoft.com/webdev/2016/11/16/announcing-asp-net-core-1-1/
Identity adds cookie authentication automatically. You're adding it a second time in Configure.
When you add the second instance you're setting both the automatic properties, so now two middlewares are trying to do the redirection, and that behaviour is "undefined" (where undefined == "Going to seriously mess things up").
This line inside Configure method in Startup class, resolve me problem:
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseAuthentication(); // <= This line
app.UseMvc(routes =>
{
...
});
}
}

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);
}
}