Simple routing with web server in netcore console app - c#

I'm having trouble getting routing to work with kestrel.
I can't find any good tutorials on how to implement this inside of a netcore console app.
I want to build a simple web server that will have 2-3 end-points that I can access.
public class WebServer
{
public static void Init()
{
IWebHostBuilder builder = CreateWebHostBuilder(null);
IWebHost host = builder.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.Build();
return WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:5000")
.UseConfiguration(config)
.UseStartup<Startup>();
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
// ????
}
public void Configure(IApplicationBuilder app)
{
// ????
}
}
}

File > New Project > Empty ASP.NET Core application.
In order to run it in a console application, make sure you select the name of you project in the "Run" dropdown in Visual Studio.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
namespace WebApplication7
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
public class MyEndpoint : Controller
{
[Route("")]
public IActionResult Get()
{
return new OkResult();
}
}
}

Related

Avoid using the WebBulder and use the Startup file

I've a .NET Core application that needs to peform operation based on a scheduler.
I've used the following code which also installs Kestrel but I don't need to use it at all
public class Program
{
public static void Main(string[] args)
{
var processModule = System.AppDomain.CurrentDomain.BaseDirectory;
var assemblyName = Assembly.GetCallingAssembly().GetName();
var version = assemblyName.Version;
Directory.SetCurrentDirectory(processModule);
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var applicationName = configuration.GetValue<string>("Properties:Application");
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration).Enrich.WithProperty("Version", version).Enrich
.WithProperty("ApplicationName", applicationName)
.CreateLogger();
Log.Logger = logger;
Log.Logger.Information("Started {ApplicationName} with version : {Version}", applicationName, version);
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }).UseSerilog()
.UseWindowsService();
}
And the Startup.cs is as follow :
class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
DataConnection.DefaultSettings = new Linq2DBSettings(configuration);
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//OMISS
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
Is there a way I can have Startup.cs (or IServiceCollection ) so that I can initialize my DI in this way?
Thanks
If you have all your services available in separate libraries, or you at least have the option to move them there from Web app, you could create some extension to configure DI both in your Web and Console applications
Library project
using Microsoft.Extensions.DependencyInjection;
namespace ClassLibrary
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection ApplyMyServices(this IServiceCollection services)
{
services.AddScoped<MyService>();
return services;
}
}
public class MyService
{ }
}
Console app
using ClassLibrary;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.ApplyMyServices();
var serviceProvider = serviceCollection.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var myService = scope.ServiceProvider.GetService<MyService>();
}
}
}
Web app
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.ApplyMyServices();
}

Building interactive console app with Web SDK

I'm going my first steps with .NET Core 3.1 by trying to build an agent/client for the Hyperledger Indy project. They provide a dotnet framework.
The used SDK is Microsoft.NET.Sdk.Web.
Heres my simple application:
Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace issuer
{
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("====== Start ======"); // Printed to console
CreateHostBuilder(args).Build().Run();
var input = System.Console.ReadKey(); // Never reached
System.Console.Write(" --- You pressed " + input.Key.ToString());
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
And Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace issuer
{
class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAriesFramework(builder =>
{
builder.RegisterAgent(options =>
{
options.EndpointUri = "http://localhost:5000";
// ...
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAriesFramework();
}
}
}
When I start it, I have a passive application listening on localhost:5000 for incoming requests from other clients.
What I want to achieve is an interactive console (instead of a web frontend) to actively initiate communication with other clients. I think a good first step would be to get a Console.ReadKey() after everything has been setup.
Is that even possible?

ASP.NET Core Testing - No method 'public static IHostBuilder CreateHostBuilder(string[] args)

I'm trying to setup my application in tests and use in Startup's Configure method context.Database.EnsureCreated() and expecting Sqlite file appear in Test's bin folder
Here's my code:
using Microsoft.AspNetCore.Mvc.Testing;
using System.Threading.Tasks;
using Xunit;
namespace MyApp.Tests
{
public class UnitTest1 : IClassFixture<CustomWebApplicationFactory<FakeStartup>>
{
private readonly CustomWebApplicationFactory<FakeStartup> _factory;
public UnitTest1(CustomWebApplicationFactory<FakeStartup> factory)
{
_factory = factory;
}
[Fact]
public async Task Test1()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync("https://localhost:5001/");
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType.ToString());
}
}
}
Which is using WebAppFactory:
using MyApp.Tests;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseStartup<FakeStartup>();
}
}
Where FakeStartup is:
using MyApp.Database;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace MyApp.Tests
{
public class FakeStartup
{
public FakeStartup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<Context>(x => x.UseSqlite($"filename={Guid.NewGuid():N}.db"));
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" });
});
}
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, Context context)
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API v1");
c.RoutePrefix = string.Empty;
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseAuthentication();
app.UseCors(x =>
{
x.AllowAnyOrigin();
x.AllowAnyMethod();
x.AllowAnyHeader();
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Here's problem
Message:
System.InvalidOperationException : No method 'public static IHostBuilder CreateHostBuilder(string[] args)' or 'public static IWebHostBuilder CreateWebHostBuilder(string[] args)' found on 'AutoGeneratedProgram'. Alternatively, WebApplicationFactory`1 can be extended and 'CreateHostBuilder' or 'CreateWebHostBuilder' can be overridden to provide your own instance.
Stack Trace:
WebApplicationFactory`1.CreateWebHostBuilder()
WebApplicationFactory`1.EnsureServer()
WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers)
WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options)
WebApplicationFactory`1.CreateClient()
UnitTest1.Test1() line 20
--- End of stack trace from previous location where exception was thrown
What may be causing this? thanks in advance
Updated with comment from CoreyP:
If you are getting this error and you're on .NET 6.0, you might need to update the Microsoft.AspNetCore.Mvc.Testing package, see this question: Integration test for ASP.NET Core 6 web API throws System.InvalidOperationException
Solution:
Create CustomWebApplicationFactory this way
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override IHostBuilder CreateHostBuilder()
{
var builder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(x =>
{
x.UseStartup<FakeStartup>().UseTestServer();
});
return builder;
}
}
Found here:
https://thecodebuzz.com/no-method-public-static-ihostbuilder-createhostbuilder/
I was getting this error because I had not followed the MS prerequisites closely enough. In my case I had not updated the Project SDK in the test csproj file. It needs to be <Project Sdk="Microsoft.NET.Sdk.Web"> (note the '.Web' on the end).

Hosting Web API and another custom service in a .NET Core Console application

I premise that I'm new to .NET Core and usually in Full Framework I used TopShelf to use console app as service.
Now I've successfully created my .NET Core application that serves WebApi, but I need to attach another service I wrote (it's an IBMMQ service that receives messages and dispatches them back).
My current program.cs is
class Program
{
static void Main(string[] args)
{
DbProviderFactories.RegisterFactory("System.Data.SqlClient", SqlClientFactory.Instance);
Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
var host = WebHost.CreateDefaultBuilder()
// .UseContentRoot(pathToContentRoot)
.UseStartup<Startup>()
.Build();
host.Run();
}
}
and here's my startup
class Startup
{
private readonly Container container = new Container();
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
IntegrateSimpleInjector(services);
services.AddControllers();
services.AddSimpleInjector(container);
services.BuildServiceProvider(validateScopes: true)
.UseSimpleInjector(container);
}
private void IntegrateSimpleInjector(IServiceCollection services)
{
container.Options.DependencyInjectionBehavior =
new SerilogContextualLoggerInjectionBehavior(container.Options);
services.AddHttpContextAccessor();
services.AddSingleton<IControllerActivator>(
new SimpleInjectorControllerActivator(container));
services.EnableSimpleInjectorCrossWiring(container);
services.UseSimpleInjectorAspNetRequestScoping(container);
}
public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env)
{
InitializeContainer(app);
DataConnection.DefaultSettings = new Linq2DbSettings();
LinqToDB.Common.Configuration.Linq.AllowMultipleQuery = true;
app.UseRouting();
app.UseEndpoints(endpointRouteBuilder => endpointRouteBuilder.MapControllers());
container.RegisterMvcControllers(app);
container.Verify();
}
private void InitializeContainer(IApplicationBuilder app)
{
//register container
}
}
Is there a way I can add my service's startup here?
Thanks

Dependency injection in ASP.NET Core 2 issue

Update
I changed the startup to :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Actio.Api.Handlers;
using Actio.Api.Repositories;
using Actio.Common.Auth;
using Actio.Common.Events;
using Actio.Common.Mongo;
using Actio.Common.RabbitMq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Actio.Api
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddJwt(Configuration);
services.AddRabbitMq(Configuration);
services.AddMongoDB(Configuration);
services.AddScoped<IEventHandler<ActivityCreated>, ActivityCreatedHandler>();
services.AddScoped<IActivityRepository, ActivityRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// app.ApplicationServices.GetService<IDatabaseInitializer>().InitializeAsync();
using (var serviceScope = app.ApplicationServices.CreateScope())
{
serviceScope.ServiceProvider.GetService<IDatabaseInitializer>().InitializeAsync();
}
app.UseAuthentication();
app.UseMvc();
}
}
}
But now I am having Error in SubscribeToEvent:
Cannot resolve scoped service
'Actio.Common.Events.IEventHandler`1[Actio.Common.Events.ActivityCreated]' from root provider.'
in my ServiceHost.cs.
ServiceHost.cs
using System;
using Actio.Common.Commands;
using Actio.Common.Events;
using Actio.Common.RabbitMq;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using RawRabbit;
namespace Actio.Common.Services
{
public class ServiceHost : IServiceHost
{
private readonly IWebHost _webHost;
public ServiceHost(IWebHost webHost)
{
_webHost = webHost;
}
public void Run() => _webHost.Run();
public static HostBuilder Create<TStartup>(string[] args) where TStartup : class
{
Console.Title = typeof(TStartup).Namespace;
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
var webHostBuilder = WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<TStartup>();
return new HostBuilder(webHostBuilder.Build());
}
public abstract class BuilderBase
{
public abstract ServiceHost Build();
}
public class HostBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public HostBuilder(IWebHost webHost)
{
_webHost = webHost;
}
public BusBuilder UseRabbitMq()
{
_bus = (IBusClient)_webHost.Services.GetService(typeof(IBusClient));
return new BusBuilder(_webHost, _bus);
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
public class BusBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public BusBuilder(IWebHost webHost, IBusClient bus)
{
_webHost = webHost;
_bus = bus;
}
public BusBuilder SubscribeToCommand<TCommand>() where TCommand : ICommand
{
var handler = (ICommandHandler<TCommand>)_webHost.Services
.GetService(typeof(ICommandHandler<TCommand>));
_bus.WithCommandHandlerAsync(handler);
return this;
}
public BusBuilder SubscribeToEvent<TEvent>() where TEvent : IEvent
{
var handler = (IEventHandler<TEvent>)_webHost.Services
.GetService(typeof(IEventHandler<TEvent>));
_bus.WithEventHandlerAsync(handler);
return this;
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
}
}
------------------------------------------------------------------------------
I recently started learning Microservices with RabbitMQ. After much struggle I got a code sample but I am unable to run it as it is giving error :"System.InvalidOperationException: 'Cannot resolve scoped service 'Actio.Common.Mongo.IDatabaseInitializer' from root provider.'"
I wish to understand this code so that I can have better understanding of Microservices.
Code-Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Actio.Api.Handlers;
using Actio.Api.Repositories;
using Actio.Common.Auth;
using Actio.Common.Events;
using Actio.Common.Mongo;
using Actio.Common.RabbitMq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Actio.Api
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddJwt(Configuration);
services.AddRabbitMq(Configuration);
services.AddMongoDB(Configuration);
services.AddScoped<IEventHandler<ActivityCreated>, ActivityCreatedHandler>();
services.AddScoped<IActivityRepository, ActivityRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//Giving Error in below line
app.ApplicationServices.GetService<IDatabaseInitializer>().InitializeAsync();
app.UseAuthentication();
app.UseMvc();
}
}
}
Can someone please help so that I can debug and l have better understanding.
Thanks
The below error is trying to tell you that you are registering IDatabaseInitializer as a scoped service but trying to access it outside the scope:
"System.InvalidOperationException: 'Cannot resolve scoped service 'Actio.Common.Mongo.IDatabaseInitializer' from root provider.'"
Try to create a scope and then use the service like:
using (var serviceScope = app.ApplicationServices.CreateScope())
{
serviceScope.ServiceProvider.GetService<IDatabaseInitializer>().InitializeAsync();
}
The solution is to disable scope validation.
So in file src\actio.common\services\servicehost.cs between lines 31-32 add the following:
.UseDefaultServiceProvider(options => options.ValidateScopes = false)
Another alternative approach to solve the issue is changing from services.AddScoped to services.AddSingleton in program.cs, working with a singleton is gonna avoid all the scope validation as well.

Categories