How to write Web API, Self hosting, Windows Service - c#

I can't make sense of this. I keep getting an exception
Exception thrown: 'System.AggregateException' in mscorlib.dll, with a message: "Internal error in the expression evaluator." and no Inner Exception!
I can't find where the error is coming from other than when the server is being started, this Line: _server.OpenAsync().Wait();
I thought it would be a good idea to use DI and I though my problems were coming from Autofac but as you can see I have remarked it all out and I'm still getting this exception. The pertinent code is in the last method. Please take a look at the code:
using System;
using System.ServiceProcess;
using System.Threading;
using System.Reflection;
using ServicesUtilities;
using System.Web.Http;
using System.Web.Http.SelfHost;
using Autofac;
using Autofac.Integration.WebApi;
namespace SeviceMerge
{
partial class MergeService : ServiceBase, IQuasiServiceBase
{
private HttpSelfHostServer _server;
private bool _runOnStart;
public InjectionService()
{
InitializeComponent();
_runOnStart = Config.Run;
}
protected override void OnStart(string[] args)
{
WebApiListener();
}
protected override void OnStop()
{
_runOnStart = false;
_server.CloseAsync().Wait();
_server.Dispose();
}
void IQuasiServiceBase.OnStart(string[] args, bool isBatchMode)
{
OnStart(args);
}
void IQuasiServiceBase.OnStop()
{
OnStop();
}
bool IQuasiServiceBase.PauseCheck()
{
return false;
}
bool IQuasiServiceBase.StopCheck()
{
return false;
}
private void WebApiListener()
{
var config = new HttpSelfHostConfiguration("http://localhost:26675");
config.Routes.MapHttpRoute(
"Presents",
"api/{controler}/{id}",
new { id = RouteParameter.Optional }
);
_server = new HttpSelfHostServer(config);
_server.OpenAsync().Wait();
}
}
}
Here's my Controller code:
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace AEMtoParmedInject.Controllers
{
public class AemToParmedMergeController : ApiController
{
private ILogger _logger;
private IWorkTheMachine _worker;
public AemToParmedMergeController()
{
_logger = new Logger();
//_worker = worker;
}
[HttpGet]
public HttpResponseMessage Index()
{
return new HttpResponseMessage()
{
Content = new StringContent(
"<html>" +
"<head>" +
"</head>" +
"<body>" +
"<p>By click on the button below you are signaling the AEM to Parmed Merge service to perform it task</p>" +
"<form>" +
"<input type='submit' action='Index' value='Integrate AEM Content'>" +
"</form" +
"</body>" +
"</html>",
Encoding.UTF8,
"text/html"
)
};
}
}
}

Everything in .net core starts with a simple console application and here I give you 5 simple steps that you can use in order to self-host a Web API / Web APP in a worker service and host the final .exe in your windows services, even you can host in Linux Systemd with little changes.
I've created a very simple Worker-Service Web-API template that exists in my GitHub profile. The following steps work 100%, but if you faced any problem, you can ask here or you can clone the template from my GitHub and use it and read the documentation there.
Do the following steps:
Create a .net core console application.
Install packages "Microsoft.AspNetCore.App" and "Microsoft.Extensions.Hosting.WindowsServices" using NuGet.
Create a Worker.cs file which will handle your worker service. put the following codes inside:
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace WorkerServiceWebAppTemplate
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
}
Create a Startup.cs file which will handle your web host and create a simple GET API in the root address and responsible to show a simple message. you can extend it. put the following lines of codes in your Startup.cs file:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
namespace WorkerServiceWebAppTemplate
{
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseHsts();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
}
Finally to start worker service and host your Web API inside your worker service, and also in order yo allow your published .exe file to be able to be host in windows services, use the following codes in your Program.cs:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace WorkerServiceWebAppTemplate
{
class Program
{
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
private static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddHostedService<Worker>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
}

Related

How do I add SignalR to a .NET Core Windows Service?

I can find tutorials to add it to MVC, and even from 2014, an article explaining how to add it to .NET 4.7 windows service.
However with a .NET Core 3.1 windows service, I just cannot figure out how to do it.
Most tutorials seem to revolve around a Startup.cs file which does not exist in a windows service. This is the latest tutorial I could find from Microsoft but it uses a Web App rather than a windows service.
The windows service runs using this code:
var builder = new HostBuilder() .ConfigureServices((hostContext, services) => { services.AddHostedService<MyWindowsService>(); });
I assume that SignalR needs to be set up around here.
I found some evidence you can do WebApp.Start("http://localhost:8080"); but this is OWIN. The example I found (an older version of the one above) then has a Startup class but there's no indication how this class is called. It takes an IAppBuilder and there's a method on it to add SignalR. However IAppBuilder does not appear to be .NET Core, nor could I find any SignalR methods of any kind.
I wonder if anyone could point me in the right direction?
SignalR Server-side requires a server that receives web requests, Kestrel or IIS normally. So you need a web app, you can still add hosted services to your webapp, there is even an example showing a web app with SignalR and a hosted service: https://learn.microsoft.com/aspnet/core/signalr/background-services?view=aspnetcore-5.0
For people wanting to develop a self-hosted ASPNETCore SignalR hub running in a Windows service, here's my barebones code. (Disclaimer: I'm new to ASPNET Core, and I don't know whether this approach would be approved by more knowledgeable folk.) The magic is in the ".UseStartup();" call.
Create a new service project using the VS 2019 "Worker Service" C# template.
Edit the service's .csproj file and insert the lines:
<ItemGroup>
<FrameworkReference Include="Microsoft.aspNetCore.App" />
</ItemGroup>
Create a Startup class:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
namespace My.SignalRCore.Service
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddHostedService<Worker>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting(); // pre-requisite for app.UseEndpoints()
app.UseEndpoints(endpoints =>
{
string url = $"/ServerHub";
endpoints.MapHub<MyHub>(url);
});
}
}
}
Create a MyHub : Hub class:
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace My.SignalRCore.Service
{
public class MyHub : Hub
{
public ILogger<Worker> _logger = null;
public MyHub(ILogger<Worker> logger)
{
_logger = logger;
//_logger.LogInformation($"{DateTimeOffset.Now} MyHub.Constructor()");
}
public async Task ProcessClientMessage(string user, string message)
{
// process an incoming message from a connected client
_logger.LogInformation($"{DateTime.Now.ToString("hh:mm:ss.fff")} MyHub.ProcessClientMessage({user}, {message})");
}
}
}
Amend the Program class to use a "UseStartup" call:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using System;
namespace My.SignalRCore.Service
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseUrls("http://*:12457");
});
}
}
Add a hub reference (if needed) in the Worker class:
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace My.SignalRCore.Service
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IHubContext<MyHub> _signalRHub;
public Worker(ILogger<Worker> logger, IHubContext<MyHub> signalRHub)
{
_logger = logger;
_signalRHub = signalRHub;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(15000, stoppingToken);
_logger.LogInformation($"{DateTime.Now.ToString("hh:mm:ss.fff")} Sending ping to all clients");
await _signalRHub.Clients.All.SendAsync("ReceiveMessage", "Server", "ping");
}
}
}
}
That's it for the server code. I've not yet installed it as a service, but it works as a console app.
On a non-dev machine, you might need to install the APSNET CORE 3.1 runtime, it's available here:
https://dotnet.microsoft.com/download/dotnet/3.1
For the client:
Install nuget package: Microsoft.AspNetCore.SignalR.Client
Create a client class along the lines of (note: the reconnect code here isn't working):
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
namespace My.SignalRCoreClientLib
{
public class SignalRCoreClientLib
{
public EventHandler<string> MessageEvent;
private HubConnection _connection;
public async Task Connect(string serverIp, int port)
{
if (_connection == null)
{
_connection = new HubConnectionBuilder()
.WithUrl($"http://{serverIp}:{port}/ServerHub")
.Build();
_connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await _connection.StartAsync();
};
_connection.On<string, string>("ReceiveMessage", (user, message) =>
{
string fullMessage = $"{user}: {message}";
MessageEvent?.Invoke(this, fullMessage);
});
}
try
{
await _connection.StartAsync();
}
catch (Exception ex)
{
MessageEvent?.Invoke(this, $"{ex.Message}; base Exception: {ex.GetBaseException().Message}");
await Task.Delay(new Random().Next(0, 5) * 1000);
await Connect(serverIp, port);
}
}
public async Task SendMessage(string user, string message)
{
try
{
await _connection.InvokeAsync("ProcessClientMessage", user, message);
}
catch (Exception ex)
{
MessageEvent?.Invoke(this, ex.Message);
}
}
}
}
That's it. Hope this is helpful.

.Net Core Console App Logging feature - why does my Console.WriteLine appear outside start and end logging?

Found this article on .Net Core logging in a console app.
It's seems to work but I don't know why my Console.WriteLine("Doing stuff"); appears outside of the start and end logging messages.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
ConfigureServices(services);
using (ServiceProvider serviceProvider = services.BuildServiceProvider())
{
MyApplication app = serviceProvider.GetService<MyApplication>();
// Start up logic here
app.Run();
}
}
private static void ConfigureServices(ServiceCollection services)
{
services.AddLogging(configure => configure.AddConsole())
.AddTransient<MyApplication>();
}
}
public class MyApplication
{
private readonly ILogger _logger;
public MyApplication(ILogger<MyApplication> logger)
{
_logger = logger;
}
internal void Run()
{
_logger.LogInformation("Application Started at {dateTime}", DateTime.UtcNow);
//Business Logic START
Console.WriteLine("Doing stuff");
//Business logic END
_logger.LogInformation("Application Ended at {dateTime}", DateTime.UtcNow);
}
}
}
Because the ConsoleLogger class in Microsoft.Extensions.Logging.Console queues the message.
It's eventually processed and printed out to the console on a background thread by an internal logger processor.
Console.WriteLine writes directly to the console.

NetCore 2.1 Generic Host as a service

I'm trying to build a Windows Service using the latest Dotnet Core 2.1 runtime. I'm NOT hosting any aspnet, I do not want or need it to respond to http requests.
I've followed the code found here in the samples: https://github.com/aspnet/Docs/tree/master/aspnetcore/fundamentals/host/generic-host/samples/2.x/GenericHostSample
I've also read this article: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.1
The code works great when run inside of a console window using dotnet run. I need it to run as a windows service. I know there's the Microsoft.AspNetCore.Hosting.WindowsServices, but that's for the WebHost, not the generic host. We'd use host.RunAsService() to run as a service, but I don't see that existing anywhere.
How do I configure this to run as a service?
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MyNamespace
{
public class Program
{
public static async Task Main(string[] args)
{
try
{
var host = new HostBuilder()
.ConfigureHostConfiguration(configHost =>
{
configHost.SetBasePath(Directory.GetCurrentDirectory());
configHost.AddJsonFile("hostsettings.json", optional: true);
configHost.AddEnvironmentVariables(prefix: "ASPNETCORE_");
configHost.AddCommandLine(args);
})
.ConfigureAppConfiguration((hostContext, configApp) =>
{
configApp.AddJsonFile("appsettings.json", optional: true);
configApp.AddJsonFile(
$"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json",
optional: true);
configApp.AddEnvironmentVariables(prefix: "ASPNETCORE_");
configApp.AddCommandLine(args);
})
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<TimedHostedService>();
})
.ConfigureLogging((hostContext, configLogging) =>
{
configLogging.AddConsole();
configLogging.AddDebug();
})
.Build();
await host.RunAsync();
}
catch (Exception ex)
{
}
}
}
#region snippet1
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
#endregion
}
EDIT: I repeat, this is not to host an ASP.NET Core app. This is a generic hostbuilder, not a WebHostBuilder.
As others have said you simply need to reuse the code that is there for the IWebHost interface here is an example.
public class GenericServiceHost : ServiceBase
{
private IHost _host;
private bool _stopRequestedByWindows;
public GenericServiceHost(IHost host)
{
_host = host ?? throw new ArgumentNullException(nameof(host));
}
protected sealed override void OnStart(string[] args)
{
OnStarting(args);
_host
.Services
.GetRequiredService<IApplicationLifetime>()
.ApplicationStopped
.Register(() =>
{
if (!_stopRequestedByWindows)
{
Stop();
}
});
_host.Start();
OnStarted();
}
protected sealed override void OnStop()
{
_stopRequestedByWindows = true;
OnStopping();
try
{
_host.StopAsync().GetAwaiter().GetResult();
}
finally
{
_host.Dispose();
OnStopped();
}
}
protected virtual void OnStarting(string[] args) { }
protected virtual void OnStarted() { }
protected virtual void OnStopping() { }
protected virtual void OnStopped() { }
}
public static class GenericHostWindowsServiceExtensions
{
public static void RunAsService(this IHost host)
{
var hostService = new GenericServiceHost(host);
ServiceBase.Run(hostService);
}
}
I hope you found the solution for this problem.
In my case I used generic host (introduced in 2.1) for such purpose and then just wrap it up with systemd to run it as a service on Linux host.
I wrote a small article about it https://dejanstojanovic.net/aspnet/2018/june/clean-service-stop-on-linux-with-net-core-21/
I hope this helps
IHostedService if for [asp.net core] backendjob,
if u want to build a windows service on .net core, u should reference this package System.ServiceProcess.ServiceController, and use ServiceBase as base class.
(you can also start from a .net framework windows service and then change the .csproj file)
edit: please see this doc and this code https://github.com/aspnet/Hosting/blob/dev/src/Microsoft.AspNetCore.Hosting.WindowsServices/WebHostWindowsServiceExtensions.cs.
To create a windows service ServiceBase for manage your IHost

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.

ASP.NET Core web api with Entity framework core not working with MySQL on ubuntu

I'm working with ASP.NET core on linux (ubuntu 16.04) and I'm trying to connect entity framework core with MySQL.
At first, I used EF without external database (I don't understand much but, I think there's an internal DB that EF use by default) and all http methods (get, post, put ...) worked.
then I connected EF with MySQL using Pomelo, this is my NuGet.config file :
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Pomelo" value="https://www.myget.org/F/pomelo/api/v3/index.json"/>
<add key="nuget.org" value="https://www.nuget.org/api/v2" />
</packageSources>
</configuration>
and this is my startup.cs file :
`
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using test.Context;
using MySql.Data.EntityFrameworkCore.Extensions;
using System.ComponentModel.DataAnnotations;
namespace test
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => {
options.AddPolicy("AllowAllHeaders",
builder => {
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddDbContext<ValuesContext>(opt =>
opt.UseMySql("server=localhost;database=test;uid=root;pwd=pfe2018"));
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("AllowAllHeaders");
app.UseMvc();
}
}
}
`
Then, I tested the project with a simple console code in program.cs and it worked.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using test.Context;
using test.Models;
namespace test
{
public class Program
{
public static void Main(string[] args) {
using (var context = new ValuesContext())
{
context.Database.EnsureCreated();
// Creating a new Value and saving it to the database
context.values.Add(new Values {
fname = "abc",
lname = "efg",
age = 100
});
var count = context.SaveChanges();
Console.WriteLine("{0} records saved to database", count);
// Retrieving and displaying data
Console.WriteLine();
Console.WriteLine("All Values in the database:");
foreach (var value in context.values)
{
Console.WriteLine("{2} |{0} | {1}", value.fname, value.lname, value.id);
}
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
the new value was successfuly added to the database, so I thought I just have to run the WEB API by changing the code in the main() method in Program.cs :
public static void Main(string[] args) {
string url = "http://localhost:5000";
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseUrls(url)
.Build();
host.Run();
}
My DbContext and Controller are as follows :
ValuesContext :
using Microsoft.EntityFrameworkCore;
using test.Models;
namespace test.Context
{
public class ValuesContext : DbContext
{
public ValuesContext(DbContextOptions<ValuesContext> options) : base(options) { }
public ValuesContext() {}
public DbSet<Values> values { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder.UseMySQL("server=localhost;database=test;uid=root;pwd=pfe2018");
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Values>(entity => {
entity.HasKey(v => v.id).HasName("VALUE_ID");
});
}
}
}
ValuesController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using test.Context;
using test.Models;
namespace test.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller {
private readonly ValuesContext _context;
public ValuesController(ValuesContext context) {
_context = context;
_context.Database.EnsureCreated();
}
// GET api/values
[HttpGet]
public IEnumerable<Values> GetAll()
{
Console.WriteLine(_context.values.ToList());
return _context.values.ToList();
}
[HttpPost]
public IActionResult Post([FromBody]Values value) {
if (value == null) {
return BadRequest();
}
_context.values.Add(value);
_context.SaveChanges();
return new CreatedAtRouteResult("itemRoute", new {id = value.id} , value);
}
}
}
when I try GET method with cURL :
curl -I http://localhost:5000/api/values
this is the response status :
HTTP/1.1 404 Not Found
Date: Thu, 22 Feb 2018 16:46:07 GMT
Server: Kestrel
any suggestion would be helpfull.

Categories