In MVC5, I had a console application that would use Microsoft.Owin.Hosting.WebApp.Start(...) to host a bunch of controllers that would be dynamically loaded from assemblies placed in an external folder and run some custom initialization on them via API call. This way I could pass parameters to the initialization method that were determined at runtime (and would not be as clunky as maintaining config files).
In MVC6, the self-hosting is now done, as far as I know, by the DNX runtime using Microsoft.AspNet.Hosting, but this is all done via command line. Is there a way I can self-host from within a C# console application so I can keep this initialization architecture?
...I had a console application that would use Microsoft.Owin.Hosting.WebApp.Start(...) to host [and to] pass parameters to the initialization method that were determined at runtime...
In ASP.NET 4.x we self-host within a console application using an OWIN host. We run our MyApp.exe directly. Its Main() method calls WebApp.Start() to create the OWIN host. We use an instance of an IAppBuilder to build up the HTTP pipeline via appBuilder.Use() and chain it all together with appBuilder.Build(). This is all within the Microsoft.Owin.Hosting namespace.
Is there a way I can self-host from within a C# console application so I can keep this initialization architecture?
In ASP.NET Core rc2 we self-host inside a console application using an IWebHost. (This is not an OWIN host though OWIN inspired it.) We run our MyApp.exe directly. The Main() method creates a new WebHostBuilder(), which we use to build up the HTTP pipeline via webHostBuilder.Use(), chaining it all together with webHostBuilder.Build(). This is all within the Microsoft.AspNet.Hosting namespace.
Regarding Pinpoint's answer, in ASP.NET Core rc1 we need to run dnx.exe instead of running our app directly. The work of the WebHostBuilder is hidden inside the dnx.exe executable. Dnx.exe also starts-up our application. Our application's Main() method calls WebApplication.Run(), after which we use an instance of IApplicationBuilder to add middleware to the HTTP pipeline via calls to appBuilder.Use(). Both our application and dnx.exe shared the responsibility of creating/configuring the host. It's convoluted and I am glad that this changed in rc2. I supposed that in rc1 the equivalent of OWIN's WebApp.Start() is WebApplication.Run().
ASP.NET 4.x ASP.NET Core rc1 ASP.NET Core rc2
N/A Dnx.exe N/A
MyApp.exe MyApp.dll MyApp.exe
Main(args) Main(args) Main(args)
WebApp.Start() WebApplication.Run(args) N/A
appBuilder.Use() appBuilder.Use() webHostBuilder.Use()
appBuilder.Build() N/A webHostBuilder.Build()
Some References
http://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api
https://msdn.microsoft.com/en-us/library/microsoft.owin.hosting.webapp%28v=vs.113%29.aspx
Katana's WebApp static class has been replaced by WebHostBuilder, that offers a much more flexible approach: https://github.com/aspnet/Hosting/blob/dev/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs.
You've probably already used this API without realizing it, as it's the component used by the hosting block when you register a new web command in your project.json (e.g Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:54540) and run it using dnx (e.g dnx . web):
namespace Microsoft.AspNet.Hosting
{
public class Program
{
private const string HostingIniFile = "Microsoft.AspNet.Hosting.ini";
private const string ConfigFileKey = "config";
private readonly IServiceProvider _serviceProvider;
public Program(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Main(string[] args)
{
// Allow the location of the ini file to be specified via a --config command line arg
var tempBuilder = new ConfigurationBuilder().AddCommandLine(args);
var tempConfig = tempBuilder.Build();
var configFilePath = tempConfig[ConfigFileKey] ?? HostingIniFile;
var appBasePath = _serviceProvider.GetRequiredService<IApplicationEnvironment>().ApplicationBasePath;
var builder = new ConfigurationBuilder(appBasePath);
builder.AddIniFile(configFilePath, optional: true);
builder.AddEnvironmentVariables();
builder.AddCommandLine(args);
var config = builder.Build();
var host = new WebHostBuilder(_serviceProvider, config).Build();
using (host.Start())
{
Console.WriteLine("Started");
var appShutdownService = host.ApplicationServices.GetRequiredService<IApplicationShutdown>();
Console.CancelKeyPress += (sender, eventArgs) =>
{
appShutdownService.RequestShutdown();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
appShutdownService.ShutdownRequested.WaitHandle.WaitOne();
}
}
}
}
https://github.com/aspnet/Hosting/blob/dev/src/Microsoft.AspNet.Hosting/Program.cs
Related
I'm using a clear architecture for a NET7 project with Blazor. In the Persistence layer, I have a function to register the database and all the repositories
public static class PersistenceServiceRegistration
{
public static IServiceCollection AddPersistenceServices(
this IServiceCollection services,
IConfiguration configuration,
string cnnStringName = "LIUContextConnection")
{
var cnnString = configuration.GetConnectionString(cnnStringName);
services.AddDbContext<LIUContext>(options =>
options.UseSqlServer(cnnString)
);
services.AddScoped<IArticleRepository, ArticleRepository>();
return services;
}
}
So, in the Program.cs I can register the persistence like
builder.Services.AddPersistenceServices(builder.Configuration);
I want to be sure that the database is created before the application starts. I added the following code an the end of the Program.cs
var app = builder.Build();
LIUContext dbcontext = app.Services.GetRequiredService<LIUContext>();
dbcontext.Database.EnsureCreated();
await app.RunAsync();
When I run the application, I get an error because
Microsoft.Data.SqlClient is not supported on this platform
I added in the Client project and in the Persistence project the NuGet package but I get the some error.
If I create the database from the Package Manager Console is working.
"is not supported on this platform" is of course the main point.
The platform here is dotnet in the Browser and a lot of APIs are not (cannot be) supported. The SqlClient needs sockets and they are not allowed in the browser. Also, you wouldn't be able to keep the credentials secret.
There is some limited support for SqLite and you have the built-in IndexedDb.
For a full SQL Db, the common approach is to use a WebService (Blazor Wasm Hosted template) to handle the Db access.
I'm using ASP.NET Core data protection with default DI behavior. That works fine when my ASP.NET application is hosted on IIS. Now I have an application that needs to run as a service. So I'm using Microsoft.Extensions.Hosting.WindowsServices to do the windows service part with our standard
Host.CreateDefaultBuilder(args)
.UseWindowsService()
The BackgroundService then hosts ASP.NET Core with your standard
var builder = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("secrets.json", optional: true, reloadOnChange: true);
}
.ConfigureWebHostDefaults(....)
inside the background service, I can then resolve an instance of IDataProtectionProvider, create a protector, and use it to unprotect my secrets
var dataProtectionProvider = Container.Resolve<Microsoft.AspNetCore.DataProtection.IDataProtectionProvider>();
var protector = dataProtectionProvider.CreateProtector(appName);
var decryptedSecret = protector.Unprocect(some secret)
Now that all works fine as long as I run my application from the CLI. But running it as a service (same file, same location, and of course under the same account), I get an 'invalid payload' exception when I call Unprotect.
I know same path and same account is important, so that's taken care of. I also know that the application can find secrets.json as I wrote some probing code that checks if the file is present and can be read before I even try to unprotect. I'm even checking if the string I'm trying to unprotect is null/empty (which it isn't).
I finally installed a debug build as a service and attached the debugger, and when I look at IDataProtectionProvider, it has a Purpose.. and when running as a service, that's c:\windows\system32. When my app runs from the CLI, it's the path to the exe. So, is there a way to specify the purpose on my own so things behave the same regardless of CLI/Service?
So how can I control the purpose?
So having noted the difference in purpose of the IDataProtectionProvider, I was well on my way of solving this. The solution was to set a static purpose as explained here
I am trying to digest a Xamarin.Forms app (developed by James Montemagno) on which the Generic Host Builder is applied.
The following is the Init method extracted from this line:
public static App Init(Action<HostBuilderContext, IServiceCollection> nativeConfigureServices)
{
// others are removed for simplicity
var host = new HostBuilder()
.ConfigureServices((c, x) =>
{
nativeConfigureServices(c, x);
ConfigureServices(c, x);
})
.ConfigureLogging(l => l.AddConsole(o =>
{
o.DisableColors = true;
}))
.Build();
App.ServiceProvider = host.Services;
return App.ServiceProvider.GetService<App>();
}
Note: Init have not run the host but it will be invoked from Android project as follows:
protected override void OnCreate(Bundle savedInstanceState)
{
// others are removed for simplicity.
LoadApplication(Startup.Init(ConfigureServices));
}
Question
Now if I compare with Asp.net Core, we know that IHost.Run() is invoked in Program.Main. The question is:
Who invokes IHost.Run() in the Xamarin.Forms app above?
From the way I understand it, this setup does not make use of actually running the host. Instead, it just relies to building it to make sure that DI and all the other related services are available and can be used by Xamarin.
Starting a host actually doesn’t do that much with the generic host. It will mostly just start the host lifetime and run hosted services (like the ASP.NET Core application would be).
But in Xamarin, starting the XAML application is something that already works on its own. So it doesn’t need to be “hosted” (although I rather think that we simply cannot have it owned by the host yet).
So this setup just makes use of the host environment to enable DI, configuration and logging, instead of using the host capabilities to actually run things. This also means that with this setup you probably won’t be able to run other hosted services within the application (unless you manage a way to properly start and stop the host within the App).
I'm trying to self host a JobHost using Microsoft.Azure.WebJobs and including the Http extension but I can't seem to figure out what Uri/port it is hosting on
This is my Main method:
static void Main(string[] args)
{
var config = new JobHostConfiguration();
var filter = new LogCategoryFilter();
filter.DefaultLevel = LogLevel.Trace;
config.LoggerFactory = new LoggerFactory()
.AddConsole(filter.Filter);
var httpExtensionConfiguration = new HttpExtensionConfiguration();
config.UseHttp(httpExtensionConfiguration);
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
var host = new JobHost(config);
host.RunAndBlock();
}
and here is the output window when running
While the HTTP Extension will add the required bindings, services and HTTP features, it does not provide a listener, so it won't setup the host for you (it relies on an external listen you'd need to setup).
With the Azure Functions runtime, the WebHost itself is the listener. The CLI uses that implementation in order to spin up the host and expose HTTP functions. You can see this approach here:
https://github.com/Azure/azure-functions-cli/blob/f0e8121c51569d8d0551fbb9bb81fbed5a9ad64c/src/Azure.Functions.Cli/Actions/HostActions/StartHostAction.cs#L102-L112
You could have a simpler approach if you don't want to rely on the Script WebHost (the CLI leverages many of its features, so it makes sense there) by simply providing your application directly when building the host. You can look at the Startup class provided by the CLI to see how things are registered and configured with the latest bits:
https://github.com/Azure/azure-functions-cli/blob/ff45a85c462c6f1e83e04dcba13da8bcca7099c5/src/Azure.Functions.Cli/Actions/HostActions/StartHostAction.cs#L349-L374
NOTE: The extension version you're using, as well as the code I've shared are pre-release (or not even merged yet), so they're subject to change, but that's the direction we're going.
I have created a GRPC Server in C# using the example given at Link. Now I want to figure out as how should I be hosting this server so that I achieve following:
Should I make this Server a Console application or a a Windows Service. If I make it a windows Service then updating the service will be cumbersome (which is a big negative) and if I make it a console app then updating will simply need shutting down exe. But that comes with the price of closing the same by mistake. Is there any other better way?
With IIS this issue won't b there as I can simply remove the site from LB and stop the website to perform the update but since GRPC won't be a part of IIS, I am not sure what's the way to get this working.
Any references for the better architecture are welcomed.
We can use Microsoft.Extensions.Hosting pacakge to host a .net core console application by using the HostBuilder API to start building gRPC host and setting it up.
In order to run the gRPC service, we first need to start/stop Grpc.Core.Server in a hosted service. A hosted service is basically a piece of code that is run by the host when the host itself is started and the same for when it is stopped. The following code implement a GrpcHostedService to override IHostedService interface:
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.Hosting;
namespace Grpc.Host
{
public class GrpcHostedService: IHostedService
{
private Server _server;
public GrpcHostedService(Server server)
{
_server = server;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_server.Start();
return Task.CompletedTask;
}
public async Task StopAsync(CancellationToken cancellationToken) => await _server.ShutdownAsync();
}
}
In the Program.cs, use HostBuilder API to start building our grpc host and setting it up:
public class Program
{
public static async Task Main(string[] args)
{
var hostBuilder = new HostBuilder()
// Add configuration, logging, ...
.ConfigureServices((hostContext, services) =>
{
// Better to use Dependency Injection for GreeterImpl
Server server = new Server
{
Services = {Greeter.BindService(new GreeterImpl())},
Ports = {new ServerPort("localhost", 5000, ServerCredentials.Insecure)}
};
services.AddSingleton<Server>(server);
services.AddSingleton<IHostedService, GrpcHostedService>();
});
await hostBuilder.RunConsoleAsync();
}
}
By doing this, the generic host will automatically run StartAsync on our hosted service, which in turn will call StartAsync on the Server instance, essentially start the gRPC server.
When we shut down the host with Control-C, the generic host will automatically call StopAsync on our hosted service, which again will call StopAsync on the Server instance which will do some clean up.
For other configuration in HostBuilder, you can see this blog.
I'm going to add one more option.
With dot net core, you can run this as a Linux Daemon now.
Currently gRPC doesn't support integration with ASP.Net/IIS. You would need to host the server in a console or as a Windows service.
Likely you would want this to be a Windows service to make it easier to keep the server running across reboots or crashes. If you want to easily turn your console application into a Windows service I would recommend using the excellent TopShelf Nuget.
Updating the service can be done as you would a console app.
Stop the Windows service. net stop <service-name}>
Copy the updated assemblies.
Start the Windowsservice net start <service-name>
My company (Shortbar) is building the application server for a hotel management system called HOLMS on gRPC. Our setup is as follows:
HOLMS.Application is a .NET class library (assembly) that does the actual work of the server
HOLMS.Application.ConsoleRunner is a C# console application that hosts HOLMS.Application. The console runner is used by (1) developers for convenience (mentioned in the question) as well as (2) production scenarios running inside a Docker container, where the container runtime (e.g. Amazon ECS) implements job control/scaling. It follows "12 factor app" guidelines, including running itself as a single, standalone, stateless process, fast startup/shutdown, and environment-variable config injection. The system logs to stdout which gets drained however stdout is drained in the prod environment (e.g. Sumo, logstash, etc). This is how our SaaS multi-tenant solution will go into production.
HOLMS.Application.ServiceRunner packages HOLMS.Application into a Windows service, for more traditional, on-premise situations where a customer's IT group will run the service themselves. This package uses the Windows registry for configuration and relies on Windows service job control for startup/shutdown/restarts. It logs to the Windows Event Log.
The ConsoleRunner and ServiceRunner each are only about 200 lines of code; for the most part, they just wrap the Application package, and call into it.
Hope this helps.