ASP.NET Core 5.0 application using React.js and MySQL as Database - mysql

Recently I am trying new ASP.NET Core 5.0 with React.js. I want to use MySql as my database server.
Now here I want to use ASP.NET Identity Server for Membership which we previously had in ASP.NET versions.
I tried to follow a tutorial listed over here: Video Link
Now in appsettings.json here is the code
{
"ConnectionStrings": {
"DefaultConnection": "server=localhost; port=3306; database=trom; user=root; password=hello#world; CharSet=utf-8"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"IdentityServer": {
"Clients": {
"Trom": {
"Profile": "IdentityServerSPA"
}
}
},
"AllowedHosts": "*"
}
Now in the Startup.cs I am trying to make changes as suggested in video
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Trom.Data;
using Trom.Models;
namespace Trom
{
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.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
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.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
}
}
The error I am getting is
There are many ways suggested on documentation website. But nothing seems to be working out. Is there any workaround for this?

The video is a bit older and uses a Pomelo version for EF Core 3.0.
For 5.0, take a look at the sample code that we show on our repository home page:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Replace with your connection string.
var connectionString = "server=localhost;user=root;password=1234;database=ef";
// Replace with your server version and type.
// Use 'MariaDbServerVersion' for MariaDB.
// Alternatively, use 'ServerVersion.AutoDetect(connectionString)'.
// For common usages, see pull request #1233.
var serverVersion = new MySqlServerVersion(new Version(8, 0, 26));
// Replace 'YourDbContext' with the name of your own DbContext derived class.
services.AddDbContext<YourDbContext>(
dbContextOptions => dbContextOptions
.UseMySql(connectionString, serverVersion)
// The following three options help with debugging, but should
// be changed or removed for production.
.LogTo(Console.WriteLine, LogLevel.Information);
.EnableSensitiveDataLogging()
.EnableDetailedErrors()
);
}
}
For your posted code above, this translates to:
var connectionString = Configuration.GetConnectionString("DefaultConnection");
var serverVersion = ServerVersion.AutoDetect(connectionString);
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(connectionString, serverVersion));
We added a mandatory ServerVersion parameter to the UseMySql() method in 5.0, because Pomelo will make use of newer features, depending on the MySQL/MariaDB version you are using.

Related

How to use DependencyResolver in Net48 selfhosted application?

I have gotten a task that contains creating a .Net 4.8 application that contains a "HttpSelfHostServer".
I'm stuck in the quest of assigning "IServiceCollection services" to config.DependencyResolver (of type System.Web.Http.Dependencies.IDependencyResolver)
I would really like not to use autofac or other frameworks, but all guids I can find are pointing toward these frameworks. Isn't Microsoft providing a way through?
I just had to solve the same issue. This is how i did it:
First I created a new facade class to map the IServiceCollection from the host builder to the interface HttpSelfHostConfiguration supports:
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Extensions.DependencyInjection;
namespace IntegrationReceiver.WebApi
{
public class HttpSelfHostDependencyResolver : IDependencyResolver
{
private readonly IServiceProvider sp;
private readonly IServiceScope scope;
public HttpSelfHostDependencyResolver(IServiceProvider sp)
{
this.sp = sp;
this.scope = null;
}
public HttpSelfHostDependencyResolver(IServiceScope scope)
{
this.sp = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() => new HttpSelfHostDependencyResolver(sp.CreateScope());
public void Dispose() => scope?.Dispose();
public object GetService(Type serviceType) => sp.GetService(serviceType);
public IEnumerable<object> GetServices(Type serviceType) => sp.GetServices(serviceType);
}
}
This required me to get the latest NuGet package Microsoft.Extensions.DependencyInjection.Abstractions according to an answer here: How do I see all services that a .NET IServiceProvider can provide?
I then registered my HttpSelfHostServer in the service provider with this code:
services.AddSingleton(sp => new HttpSelfHostDependencyResolver(sp));
services.AddSingleton(sp =>
{
//Starting the HttpSelfHostServer with user-level permissions requires to first run a command like
// netsh http add urlacl url=http://+:8080/ user=[DOMAINNAME]\[USERNAME]
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.DependencyResolver = sp.GetRequiredService<HttpSelfHostDependencyResolver>();
return new HttpSelfHostServer(config);
});
And finally, to find my ApiController, I had to register that too in the service provider. I did that simply with:
services.AddScoped<HealthCheckController>();
For brewity, I'm just including my api controller below to illustrate how it now gets its dependencies:
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
namespace IntegrationReceiver.WebApi
{
public class HealthCheckController : ApiController
{
private readonly ServiceBusRunner serviceBusRunner;
public HealthCheckController(ServiceBusRunner serviceBusRunner)
{
this.serviceBusRunner = serviceBusRunner;
}
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var response = new
{
serviceBusRunner.RunningTasks,
serviceBusRunner.MaxRunningTasks
};
return await Json(response)
.ExecuteAsync(System.Threading.CancellationToken.None);
}
}
}
This is a pretty dumb-down implementation but works for me until I can upgrade this code to net5.
I hope it helps you too!

How to inject settings from appsettings.json to an ILoggerProvider?

I'm implementing a custom ILogger<T> due to some special requirements, and one of those requirements is to have a section, inside the Logging section in the project's appsettings.json, with configuration values for that logger. Question is, what's the right way (or a good way) to inject those settings in the logger? I presume I should start by injecting those settings in the corresponding ILoggerProvider and have the provider instantiate a logger with those settings, but I'm stumped on how to properly inject those values in the provider.
So far I have this in Program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
logging.AddConfiguration(context.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddProvider(new CustomLoggerProvider());
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
, the relevant section in appsettings.json is:
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"CustomLogger": {
"Level": "Error",
"Url": "http://[URL]"
}
},
, and the provider implementation is as follows:
[ProviderAlias("CustomLogger")]
public class CustomLoggerProvider : ILoggerProvider
{
private readonly Dictionary<string, CustomLogger> _loggers;
private readonly LogLevel _level;
private bool _already_disposed;
public LogLevel Level
{
get => _level;
}
public CustomLoggerProvider()
{
//Here's where I don't know what should I do to inject the proper config values
_level = LogLevel.Error;
}
public ILogger CreateLogger(string categoryName)
{
if (_loggers.ContainsKey(categoryName))
{
return _loggers[categoryName];
}
var l = new CustomLogger(this);
_loggers.Add(categoryName, l);
return l;
}
// Rest of implementation omitted for simplicity
}
I found an acceptable answer and I'll post it here, in case someone needs it.
Steps are:
Implement your ILoggerProvider and your ILogger. The examples on Microsoft's page are a good guide.
Have your ILoggerProvider implementation accept the needed settings on its constructor (via IOptions<T> or simple parameters; I'm using an options object called MyCustomLoggerSettings).
Register your ILoggerProvider in Startup.ConfigureServices() as a singleton, like this (let's say my implementation is called MyCustomLoggerProvider):
services.AddSingleton<ILoggerProvider>(p =>
{
var options = p.GetService<IOptions<MyCustomLoggerSettings>>();
return new MyCustomLoggerProvider(options);
});
Done.
As a bonus, in this way you can pass any other dependencies to this object (including objects registered on the service collection; the use of method overloads in services that accept an anonymous function give you the service collection as a parameter, and there you can use GetService<T> to resolve any dependencies.

Access appsettings from an abstract class used in my test scripts

Working with asp.net core 2.0
I have created a BD test project.
I have an abstract base class structured like this:
[ExcludeFromCodeCoverage]
[Category("Integration")]
public abstract class BaseIntegrationContext : BaseIntegrationSetUp
{
protected MyDataBaseContext Context;
private IWebHostBuilder _webHostBuilder;
protected override void FixtureSetUp()
{
base.FixtureSetUp();
WebSetUp();
DbSetUp();
}
private void WebSetUp()
{
_webHostBuilder = new WebHostBuilder()
.UseStartup<Startup>();
}
private void DbSetUp()
{
base.FixtureSetUp();
//options
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var options = new DbContextOptionsBuilder<InformedWorkerDbContext>()
.UseSqlServer(config.GetConnectionString("DefaultConnection"))
.Options;
Context = new MyDataBaseContext(options);
}
}
unless I copy and add the appsettings.json to this test project I will obviously get the error that the json file cannot be found.
What is the accepted way to access appsettings.json from withing an abstract base class that use in a test project?
I have tried adding an entity model called ServiceSettings that maps to the json file in my web project:
public class ServiceSettings
{
public string DatabaseServerConnection { get; set; }
}
which i instantiate in startup.cs:
services.Configure<ServiceSettings>(Configuration.GetSection("ServiceSettings"));
My Json file looks like this:
{
"ServiceSettings": {
"DatabaseServerConnection": "Server=localhost;Initial,Catalog=InformedWorker;Integrated Security=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Information"
},
"Console": {
"LogLevel": {
"Microsoft.AspNetCore.Mvc.Razor.Internal": "Warning",
"Microsoft.AspNetCore.Mvc.Razor.Razor": "Debug",
"Microsoft.AspNetCore.Mvc.Razor": "Error",
"Default": "Information"
}
}
}
}
and in my abstract class I do this:
IOptions<ServiceSettings> myOptions = Options.Create(new ServiceSettings()
{
});
but typing myOptions the intellisense only gives me 'Value' to work with..?
unless I copy and add the appsettings.json to this test project I will obviously get the error that the json file cannot be found.
This is cause you don't specify file location in
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
.AddJsonFile registers a JSON FileProvider and path parameter ("appsettings.json" in your case) is a Path relative to the base path stored in ConfigurationBuilder.Properties. So the default base path is null and a file is expected in current working directory.
To set base path you may use .SetBasePath(<basePath>) extension method
public static IConfigurationBuilder SetBasePath(this IConfigurationBuilder builder, string basePath);
where basePath: The absolute path of file-based providers.

File Not Found Exception Visual Studio 2015

I am getting file not found error though I have the needed file inside the project directory.There are no compilation errors.
Here is my startup.cs class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
namespace OdeToFood
{
public class Startup
{
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("config.json");
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
// 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 http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// 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)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
var message = Configuration["greeting"];
await context.Response.WriteAsync(message);
});
}
}
}
And this is the error message I get when I build it.
System.IO.FileNotFoundException: The configuration file 'config.json' was not found and is not optional.
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload)
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
at OdeToFood.Startup..ctor()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
at Microsoft.AspNetCore.Hosting.Internal.StartupLoader.LoadMethods(IServiceProvider services, Type startupType, String environmentName)
at Microsoft.AspNetCore.Hosting.WebHostBuilderExtensions.<>c__DisplayClass1_0.b__1(IServiceProvider sp)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureStartup()
at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
Screenshot showing the file present
How should I rectify this?
Thanks
I think you forget to use Server.MapPath function.
You can edit your Constructor with following code :
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile(HttpContext.Current.Server.MapPath("~/config.json"));
Configuration = builder.Build();
}
Hope it helps you :)

vNext: Console app that uses razor views without hosting

I am creating console application that does some file conversions. These conversions are easily done creating a model from the input file and then executing razor models for the output.
To have this working in the IDE I used Visual Studio 2015 preview and created a vnext console application that uses MVC. (You get razor support out of the box then). To get this all working you need to host the MVC app though, and the cheapest way to do that is hosting is through a WebListener. So I host the MVC app and then call it through "http://localhost:5003/etc/etc" to get the rendered views that construct the output.
But the console app is not supposed to listen to/use a port. It is just a command line tool for file conversions. If multiple instances would run at the same time they would fight to host the pages on the same port. (This could of coarse be prevented by choosing a port dynamically, but this is not what I am looking for)
So my question is how would you get this working without using a port, but using as much of the vnext frameworks as possible.
In short: how can I use cshtml files that I pass models in a console app that does not use a port using the vnext razor engine.
Here is some code I currently use:
Program.cs
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ConsoleTest
{
public class Program
{
private readonly IServiceProvider _hostServiceProvider;
public Program(IServiceProvider hostServiceProvider)
{
_hostServiceProvider = hostServiceProvider;
}
public async Task<string> GetWebpageAsync()
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("http://localhost:5003/home/svg?idx=1");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
return await httpClient.GetStringAsync("");
}
}
public Task<int> Main(string[] args)
{
var config = new Configuration();
config.AddCommandLine(args);
var serviceCollection = new ServiceCollection();
serviceCollection.Add(HostingServices.GetDefaultServices(config));
serviceCollection.AddInstance<IHostingEnvironment>(new HostingEnvironment() { WebRoot = "wwwroot" });
var services = serviceCollection.BuildServiceProvider(_hostServiceProvider);
var context = new HostingContext()
{
Services = services,
Configuration = config,
ServerName = "Microsoft.AspNet.Server.WebListener",
ApplicationName = "ConsoleTest"
};
var engine = services.GetService<IHostingEngine>();
if (engine == null)
{
throw new Exception("TODO: IHostingEngine service not available exception");
}
using (engine.Start(context))
{
var tst = GetWebpageAsync();
tst.Wait();
File.WriteAllText(#"C:\\result.svg", tst.Result.TrimStart());
Console.WriteLine("Started the server..");
Console.WriteLine("Press any key to stop the server");
Console.ReadLine();
}
return Task.FromResult(0);
}
}
}
Startup.cs
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.ConfigurationModel;
namespace ConsoleTest
{
public class Startup
{
public IConfiguration Configuration { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
// Add MVC services to the services container
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
//Configure WebFx
app.UseMvc(routes =>
{
routes.MapRoute(
null,
"{controller}/{action}",
new { controller = "Home", action = "Index" });
});
}
}
}
I solved it using the following code:
Program.cs
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.TestHost;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.Runtime.Infrastructure;
namespace ConsoleTest
{
public class Program
{
private Action<IApplicationBuilder> _app;
private IServiceProvider _services;
public async Task<string> TestMe()
{
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
return await client.GetStringAsync("http://localhost/home/svg?idx=1");
}
public void Main(string[] args)
{
_services = CallContextServiceLocator.Locator.ServiceProvider;
_app = new Startup().Configure;
var x = TestMe();
x.Wait();
Console.WriteLine(x.Result);
Console.ReadLine();
}
}
}
Startup.cs
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Routing;
namespace ConsoleTest
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseServices(services =>
{
// Add MVC services to the services container
services.AddMvc();
});
//Configure WebFx
app.UseMvc(routes =>
{
routes.MapRoute(
null,
"{controller}/{action}",
new { controller = "Home", action = "Index" });
});
}
}
}