Custom IServer implementation does not work with IISIntegration - asp.net

I've a sample asp.net core app with custom Implementation of IServer interface as following:
namespace AspNetCoreAppWithOwinHttpListenerServer
{
public class Program
{
public static void Main(string[] args)
{
IWebHost host = new WebHostBuilder()
.UseHttpListener()
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
public class OwinHttpListenerServer : IServer
{
private IDisposable _HttpListenerServer;
public IFeatureCollection Features { get; } = new FeatureCollection();
public OwinHttpListenerServer()
{
Features.Set<IServerAddressesFeature>(new ServerAddressesFeature());
}
public void Start<TContext>(IHttpApplication<TContext> application)
{
Func<IDictionary<string, object>, Task> appFunc = async env =>
{
FeatureCollection features = new FeatureCollection(new OwinFeatureCollection(env));
TContext context = application.CreateContext(features);
try
{
await application.ProcessRequestAsync(context);
}
catch (Exception ex)
{
application.DisposeContext(context, ex);
throw;
}
application.DisposeContext(context, null);
};
appFunc = OwinWebSocketAcceptAdapter.AdaptWebSockets(appFunc);
Dictionary<string, object> props = new Dictionary<string, object>();
props["host.Addresses"] = Features
.Get<IServerAddressesFeature>()
.Addresses
.Select(add => new Uri(add))
.Select(add => new Address(add.Scheme, add.Host, add.Port.ToString(), add.LocalPath).Dictionary)
.ToList();
OwinServerFactory.Initialize(props);
_HttpListenerServer = OwinServerFactory.Create(appFunc, props);
}
public void Dispose()
{
_HttpListenerServer?.Dispose();
}
}
public static class OwinHttpListenerWebHostBuilderExtensions
{
public static IWebHostBuilder UseHttpListener(this IWebHostBuilder builder)
{
return builder.ConfigureServices(services =>
{
services.AddSingleton<IServer, OwinHttpListenerServer>();
});
}
}
}
It works fine without IIS, but running on IIS or IISExpress results in "did not listen on the given port" by VSIISExeLauncher.exe.
How can I make my custom server compatible with IIS?
Thanks in advance.
GitHub repository: https://github.com/ymoradi/AspNetCoreAppWithOwinHttpListenerServer

AspNetCoreModule (IIS) is incompatible with Http.Sys based servers. See https://github.com/aspnet/AspNetCoreModule/issues/23

OwinHttpListener is based on .NET HttpListenerClass and that class is implemented by help of http.sys.
http.sys is a kernel mode code, and for now, there is no support for that in asp.net core iis module.

Related

Multiple registrations in .net core DI possible implications

Will anything bad happen if there are multiple registrations of services in .net core's DI? For example let's say we have the following code
public void ConfigureServices(IServiceCollection services)
{
//....
services.AddHealthChecks();
//...
}
And in another (extension maybe) class we use services.AddHealthChecks() again. Will this mess the DI's container or not?
Thanks in advance
You can register a service several times without an exception has thrown. The problem is when you register a service several times with different scope. consider following example:
public interface IMyInterface
{
void Print();
}
public class MyInterface : IMyInterface
{
public void Print()
{
}
}
And let's register IMyInterface with two different scopes:
internal class Program
{
private static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
services.AddScoped<IMyInterface, MyInterface>();
services.AddSingleton<IMyInterface, MyInterface>();
var provider = services.BuildServiceProvider();
for (var i = 0; i < 5; i++)
{
var scope = provider.CreateScope();
using (scope)
{
var myInterface = scope.ServiceProvider.GetService<IMyInterface>();
Console.WriteLine(myInterface.GetHashCode());
}
}
}
}
First, register IMyInterface in the following order:
services.AddScoped<IMyInterface, MyInterface>();
services.AddSingleton<IMyInterface, MyInterface>();
As you can see we get a singleton instance of MyInterface and the hashcode is the same.
Now let's change it to this:
services.AddSingleton<IMyInterface, MyInterface>();
services.AddScoped<IMyInterface, MyInterface>();
Now we get the scoped type of MyInterface and the hashcode is different each time. You always get the last registered scope of your type.
Look at AddHealthChecks code:
public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services)
{
services.TryAddSingleton<HealthCheckService, DefaultHealthCheckService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, HealthCheckPublisherHostedService>());
return new HealthChecksBuilder(services);
}
By adding services.AddHealthChecks(); more than one time, you just registering HealthCheckService and IHostedService as a singleton services and I think it doesn't affect the health check functionality.
With the following extension, you can find duplicate registration:
public static class ServiceDescription
{
private static List<IGrouping<Type, ServiceDescriptor>> Descriptors;
public static IHostBuilder ConfigureServiceDescriptionCheck(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices(services =>
{
Descriptors = services.Where(i => !i.ServiceType.Assembly.FullName.Contains("Microsoft"))
.GroupBy(p => p.ServiceType)
.Where(x => x.Count() > 1).ToList();
});
return hostBuilder;
}
public static IHost UseServiceDescriptionCheck(this IHost host)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
Descriptors.ForEach(item =>
{
var count = item.Count();
logger.LogWarning("Service of type {Key} has been registered {count} times", item.Key, count);
});
return host;
}
}
And use it in this way:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().UseServiceDescriptionCheck().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureServiceDescriptionCheck();
}
Read this article to get more details.

Seeding Database in Asp.net Core, cannot create admin account

I'm trying to create a default admin account when I start an application.
Now what I'm interested in is how to seed a database in asp.net core. I have a seed code that I run in the main program. It shows no error but does not update the database. I've been trying to change "Identity rolls" to Application Role in my SeedData, but it has no effect at all.
I wouldn't want to change most of the code and I know it can be done with a model builder, but I don't want it that way. I think the problem is with the main program, but I don't understand what I need to change. My code is shown here.
SeedData.cs
namespace AspNetCoreTodo
{
public static class SeedData
{
public static async Task InitializeAsync(IServiceProvider services)
{
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await EnsureRolesAsync(roleManager);
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
await EnsureTestAdminAsync(userManager);
}
private static async Task EnsureRolesAsync(RoleManager<IdentityRole> roleManager)
{
var alreadyExists = await roleManager.RoleExistsAsync(Constants.AdministratorRole);
if (alreadyExists) return;
await roleManager.CreateAsync(new IdentityRole(Constants.AdministratorRole));
}
private static async Task EnsureTestAdminAsync(UserManager<ApplicationUser> userManager)
{
var testAdmin = await userManager.Users
.Where(x => x.UserName == "admin#todo.local")
.SingleOrDefaultAsync();
if (testAdmin != null) return;
testAdmin = new ApplicationUser { Email = "admin#todo.local", UserName = "admin#todo.local" };
await userManager.CreateAsync(testAdmin, "NotSecure123!!");
await userManager.AddToRoleAsync(testAdmin, Constants.AdministratorRole);
}
}
}
ApplicationDbContext.cs
namespace AspNetCoreTodo.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<TodoItem> Items {get; set;}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}
Program.cs
namespace AspNetCoreTodo
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
private static void InitializeDatabase(IWebHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedData.InitializeAsync(services).Wait();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Startup.cs //Configuration
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddMvc();
services.AddAuthentication();
services.AddScoped<ITodoItemService, TodoItemService>();
}
Does your application ever goes in the method InitializeDatabase(IWebHost host) in Program.cs?
Could you please try to call your method in Main() method:
public static void Main(string[] args)
{
var webHost = CreateWebHostBuilder(args).Build();
InitializeDatabase(webHost);
webHost.Run();
}
Note: You have to create that 'webHost' variable, because your method takes 'IWebHost' as a parameter. And CreateWebHostBuilder(string[] args) method returns type of IWebHostBuilder. Also Run() method will work on type of IWebHost.
Note: As Nilay noticed above, I'd also seed my database in Startup.cs, in the
if(env.isDevelopment){
InitializeDatabase(webHost);
}
Because normally, seeding is a "development" purpose.

SignalR Hub in C# class

Please tell me how I can use SignalR in not controller class.
I'm using AspNetCore.SignalR 1.0.2.
For example my Hub:
public class EntryPointHub : Hub
{
public async Task Sended(string data)
{
await this.Clients.All.SendAsync("Send", data);
}
}
In my job class (hangfire) SignalR doesn't work, my frontend not recieved messages.
public class UpdateJob
{
private readonly IHubContext<EntryPointHub> _hubContext;
public UpdateJob(IHubContext<EntryPointHub> hubContext)
{
_hubContext = hubContext;
}
public void Run()
{
_hubContext.Clients.All.SendAsync("Send", "12321");
}
}
But it In my controller works well.
...
public class SimpleController: Controller
{
private readonly IHubContext<EntryPointHub> _hubContext;
public SimpleController(IHubContext<EntryPointHub> hubContext)
{
_hubContext = hubContext;
}
[HttpGet("sendtoall/{message}")]
public void SendToAll(string message)
{
_hubContext.Clients.All.SendAsync("Send", message);
}
}
I think you are missing .net core DI mechanism for your Job Class. In Startup.cs file add that like below:
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddScoped<UpdateJob>();
}
public void Configure(IApplicationBuilder app)
{
app.UseSignalR(routes =>
{
routes.MapHub<EntryPointHub>("ephub");
});
}
Then you need to install signalr-client for client end and calling like below in js file.
let connection = new signalR.HubConnection('/ephub');
connection.on('send', data => {
var DisplayMessagesDiv = document.getElementById("DisplayMessages");
DisplayMessagesDiv.innerHTML += "<br/>" + data;
});
Hope this will help you.
Solved: Thank for comments, I implement JobActivator and send to activator constructor ServiceProvider like this (in Startup.Configure):
IServiceProvider serviceProvider = app.ApplicationServices.GetService<IServiceProvider>();
GlobalConfiguration.Configuration
.UseActivator(new HangfireActivator(serviceProvider));
And add in ConfigureServices:
services.AddTransient<UpdateJob>();

How do you use dependency injection in an asp.net console app?

I am doing something like:
private static IServiceProvider serviceProvider;
public Program(IApplicationEnvironment env, IRuntimeEnvironment runtime)
{
var services = new ServiceCollection();
ConfigureServices(services);
serviceProvider = services.BuildServiceProvider();
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
private void ConfigureServices(IServiceCollection services)
{
//Console.WriteLine(Configuration["Data:DefaultConnection:ConnectionString"]);
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
I am struggling to get to use the program using an injected DbContext. Any idea? How do you instantiate the program and get everything injected? I don't know what to do in the static Main method.
Is there an equivalent for this?
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
Something like?
public static void Main(string[] args) => ConsoleApplication.Run<Program>(args);
This is how I did it:
public class Startup
{
public static IConfigurationRoot Configuration { get; set; }
public static void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddSingleton<IMyManager, Manager>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<Program, Program>();
}
public static void Main(string[] args)
{
var services = new ServiceCollection();
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
.AddEnvironmentVariables()
.AddUserSecrets();
Configuration = builder.Build();
ConfigureServices(services);
var provider = services.BuildServiceProvider();
CancellationTokenSource ctSource = new CancellationTokenSource();
CancellationToken ct = ctSource.Token;
Task task = Task.Run(async () =>
{
Program program = provider.GetRequiredService<Program>();
await program.Run(ct);
});
try
{
task.Wait();
}
catch (AggregateException e)
{
throw e.InnerException;
}
ctSource.Cancel();
ctSource.Dispose();
}
}
Then the program is just:
class Program
{
private IMyManager _myManager;
public Program(IMyManager myManager)
{
_myManager = myManager;
}
public async Task Run(CancellationToken cancelationToken)
{
while (true)
{
cancelationToken.ThrowIfCancellationRequested();
// My things using _myManager
await Task.Delay(10000, cancelationToken);
}
}
}
I deleted a bunch of stuff for the example so it probably crashes somewhere, but you get the idea.
Just in case anyone else is looking for a small and simple example to follow.
Here is a small console app I wrote recently for a an example. It"s only a small password generator demonstration of DI in an app with unit tests.
https://github.com/AnthonySB/PasswordApplication
using System;
using Microsoft.Extensions.DependencyInjection;
using PasswordExercise.Interfaces;
using PasswordExercise.Services;
namespace PasswordExercise
{
    class Program
    {
        static void Main(string[] args)
        {
            //Dependency injection
            var serviceProvider = new ServiceCollection()
                .AddSingleton<IPasswordGeneratorService, PasswordGenerator>()
                .AddSingleton<IPasswordService, PasswordService>()
                .BuildServiceProvider();
            //Get the required service
            var passwordService = serviceProvider.GetService<IPasswordService>();
            //For reading from the console
            ConsoleKeyInfo key;
            //Display the menu
            passwordService.Menu();
            do
            {
                //Read the console key, do not display on the screen
                key = Console.ReadKey(true);
                switch (key.KeyChar.ToString())
                {
                    case "1":
                        Console.WriteLine("Simple password: {0}", passwordService.SimplePassword());
                        break;
                    case "2":
                        Console.WriteLine("Moderate password: {0}", passwordService.ModeratePassword());
                        break;
                    case "3":
                        Console.WriteLine("Strong password: {0}", passwordService.StrongPassword());
                        break;
                }
            } while (key.Key != ConsoleKey.Escape);
        }
    }
}
Hope this helps someone.

SpringBoot Undertow : how to dispatch to worker thread

i'm currently have a look a springboot undertow and it's not really clear (for me) how to dispatch an incoming http request to a worker thread for blocking operation handling.
Looking at the class UndertowEmbeddedServletContainer.class, it look like there is no way to have this behaviour since the only HttpHandler is a ServletHandler, that allow #Controller configurations
private Undertow createUndertowServer() {
try {
HttpHandler servletHandler = this.manager.start();
this.builder.setHandler(getContextHandler(servletHandler));
return this.builder.build();
}
catch (ServletException ex) {
throw new EmbeddedServletContainerException(
"Unable to start embdedded Undertow", ex);
}
}
private HttpHandler getContextHandler(HttpHandler servletHandler) {
if (StringUtils.isEmpty(this.contextPath)) {
return servletHandler;
}
return Handlers.path().addPrefixPath(this.contextPath, servletHandler);
}
By default, in undertow all requests are handled by IO-Thread for non blocking operations.
Does this mean that every #Controller executions will be processed by a non blocking thread ? or is there a solution to chose from IO-THREAD or WORKER-THREAD ?
I try to write a workaround, but this code is pretty uggly, and maybe someone has a better solution:
BlockingHandler.class
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface BlockingHandler {
String contextPath() default "/";
}
UndertowInitializer.class
public class UndertowInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
configurableApplicationContext.addBeanFactoryPostProcessor(new UndertowHandlerPostProcessor());
}
}
UndertowHandlerPostProcessor.class
public class UndertowHandlerPostProcessor implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(BlockingHandler.class));
for (BeanDefinition beanDefinition : scanner.findCandidateComponents("org.me.lah")){
try{
Class clazz = Class.forName(beanDefinition.getBeanClassName());
beanDefinitionRegistry.registerBeanDefinition(clazz.getSimpleName(), beanDefinition);
} catch (ClassNotFoundException e) {
throw new BeanCreationException(format("Unable to create bean %s", beanDefinition.getBeanClassName()), e);
}
}
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
//no need to post process defined bean
}
}
override UndertowEmbeddedServletContainerFactory.class
public class UndertowEmbeddedServletContainerFactory extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {
DeploymentManager manager = createDeploymentManager(initializers);
int port = getPort();
if (port == 0) {
port = SocketUtils.findAvailableTcpPort(40000);
}
Undertow.Builder builder = createBuilder(port);
Map<String, Object> handlers = applicationContext.getBeansWithAnnotation(BlockingHandler.class);
return new UndertowEmbeddedServletContainer(builder, manager, getContextPath(),
port, port >= 0, handlers);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
...
override UndertowEmbeddedServletContainer.class
public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manager,
String contextPath, int port, boolean autoStart, Map<String, Object> handlers) {
this.builder = builder;
this.manager = manager;
this.contextPath = contextPath;
this.port = port;
this.autoStart = autoStart;
this.handlers = handlers;
}
private Undertow createUndertowServer() {
try {
HttpHandler servletHandler = this.manager.start();
String path = this.contextPath.isEmpty() ? "/" : this.contextPath;
PathHandler pathHandler = Handlers.path().addPrefixPath(path, servletHandler);
for(Entry<String, Object> entry : handlers.entrySet()){
Annotation annotation = entry.getValue().getClass().getDeclaredAnnotation(BlockingHandler.class);
System.out.println(((BlockingHandler) annotation).contextPath());
pathHandler.addPrefixPath(((BlockingHandler) annotation).contextPath(), (HttpHandler) entry.getValue());
}
this.builder.setHandler(pathHandler);
return this.builder.build();
}
catch (ServletException ex) {
throw new EmbeddedServletContainerException(
"Unable to start embdedded Undertow", ex);
}
}
set initializer to the application context
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).initializers(new UndertowInitializer()).run(args);
}
finaly create a HttpHandler that dispatch to worker thread
#BlockingHandler(contextPath = "/blocking/test")
public class DatabaseHandler implements HttpHandler {
#Autowired
private EchoService echoService;
#Override
public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
if(httpServerExchange.isInIoThread()){
httpServerExchange.dispatch();
}
echoService.getMessage("my message");
}
}
As you can see, my "solution" is really heavy, and i would really appreciate any help to simplify it a lot.
Thank you
You don't need to do anything.
Spring Boot's default Undertow configuration uses Undertow's ServletInitialHandler in front of Spring MVC's DispatcherServlet. This handler performs the exchange.isInIoThread() check and calls dispatch() if necessary.
If you place a breakpoint in your #Controller, you'll see that it's called on a thread named XNIO-1 task-n which is a worker thread (the IO threads are named XNIO-1 I/O-n).

Resources