.NET 6 - WebAPI with EFCore - error generating migrations - ef-code-first

To demonstrate this issue I've created a small project using the webapi template in .NET 6.
The issue when trying to add my first migration, I get the following error (running with the verbose option);
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'BloggingContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'BloggingContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
As the new .NET 6 template does not have CreateHostBuilder in the startup, the EF tools are trying to create an instance of the context class, but because I'm using DI in the constructor to obtain the configuration this error arises.
My demo project;
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" />
</ItemGroup>
</Project>
Created a models class to hold the db context and entities.
using Microsoft.EntityFrameworkCore;
namespace dotnet6apiefcore;
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public IConfiguration _configuration { get; }
public BloggingContext(IConfiguration configuration)
{
this._configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder options) {
var connectString = _configuration.GetValue<string>("ConnectionString");
options.UseSqlServer(connectString);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
In the program.cs I register the BloggingContext.
using dotnet6apiefcore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<BloggingContext>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

I was running into a similar error and just was able to successfully complete my migrations.
Verify the NuGet packages for all your projects are using the same version, and since you're using .NET 6, you need to update your EF Core packages to the latest pre-release.
In the NuGet Package Manager for your projects go to the 'Updates' tab and make sure to select 'Include prerelease'. This should show some results that you're able to update to.
Once I did this, I was able to run my same migrations command and successfully complete migrations.
NOTE: This will only be accurate until the Production Release of .NET 6. After .NET 6 is released, you will need to make sure your packages are running the current release (or the corresponding version with your projects and ef core dotnet tools).

Related

Can't bind service implementation using DI inside function with ServiceBusTrigger parameter in .net core Azure Web Job

I have a .net core console application I am going to deploy as an Azure web job. The purpose of the job is to listen for new messages in an Azure Service Bus Queue. I have set the listener up using a function containing the [ServiceBusTrigger] Attribute. I built a dummy implementation which just reads the latest message from the queue - this works without issue - the message is passed correctly from the service bus queue to my function.
When I try to go to the next level and add an interface parameter to the function to be injected by DI I get an error.
Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException:
'Error indexing method 'Functions.ProcessMeasurementData''
InvalidOperationException: Cannot bind parameter 'service' to type
IProcessMeasurementService. Make sure the parameter Type is supported
by the binding. If you're using binding extensions (e.g. Azure
Storage, ServiceBus, Timers, etc.) make sure you've called the
registration method for the extension(s) in your startup code (e.g.
builder.AddAzureStorage(), builder.AddServiceBus(),
builder.AddTimers(), etc.).
This is my function. If I remove the parameter IProcessMeasurementService service it works with no issue running locally from Visual Studio 2019 (I haven't tried deploying to azure yet as a webjob), picking up new items as they are added to the Azure Service Bus Queue.
public class Functions
{
public static async Task ProcessMeasurementData(
[ServiceBusTrigger("process-measurement-data-queue", Connection = "AzureWebJobsServiceBus")] Message message,
IProcessMeasurementService service)
{
try
{
var measurements = JsonConvert
.DeserializeObject<List<CreateMeasurementInput>>
(Encoding.UTF8.GetString(message.Body));
await service.DoStuff(measurements);
// log.LogInformation(message.ContentType);
}
catch (Exception e)
{
throw;
}
}
I think I am registering the service correctly, like this:
{
// Register application services
services.AddSingleton<IProcessMeasurementService, ProcessMeasurementService>();
});
This is my main function in its entirety.
static void Main(string[] args)
{
var builder = new HostBuilder();
builder.ConfigureAppConfiguration((builder) =>
{
builder
.AddJsonFile("appsettings.json", false, true)
.AddEnvironmentVariables();
});
builder.ConfigureWebJobs(b =>
{
b.AddServiceBus(x =>
{
x.MessageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
AutoComplete = false
};
});
b.AddAzureStorageCoreServices();
});
builder.ConfigureServices(services =>
{
services.AddOptions();
// Register application services
services.AddSingleton<IProcessMeasurementService, ProcessMeasurementService>();
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
From googling it feels like the problem might be something to do with my nuget package version. I tried adding a file called "host.json" in case it was a known problem with azure function versions conflicting with extensions libraries. But it did nothing. I am not actually using AzureFunctions (serverless functions I mean) but I am clutching at straws at this stage.
Does anyone know what the problem is?
This is the host.json just in case this is the issue.
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}
}
Here're my nuget versions installed
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="4.1.2" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.16" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="4.1.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Sources" Version="3.0.16" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" />
Change from static function method to an instance member and inject the service via constructor injection directly into the function class.
public class Functions {
private readonly IProcessMeasurementService service;
public Functions (IProcessMeasurementService service) {
this.service = service;
}
public async Task ProcessMeasurementData(
[ServiceBusTrigger("process-measurement-data-queue", Connection = "AzureWebJobsServiceBus")] Message message)
{
try {
var measurements = JsonConvert
.DeserializeObject<List<CreateMeasurementInput>>
(Encoding.UTF8.GetString(message.Body));
await service.DoStuff(measurements);
// log.LogInformation(message.ContentType);
} catch (Exception e) {
//...
throw;
}
}
}

Multi-platform compilation: System.Configuration.ConfigurationManager.GetSection throws error on .NetCore

Background:
We are in the process of migrating .Net application to .Net Core.
As a strategy, we would like to keep the existing functionality intact on Full framework while migrating portion of the application to .Net Core. Full application would support .services over Net remoting and REST API whereas .Net core application will only support REST API services.
We have decided to keep the same code base for entire application and support compilation on multiple platforms (NetcoreApp2.1 and Net472).
There is a single application configuration file. Most of the components are dependent on the information stored in this file. Thus we would like to retain the single configuration file for both platforms.
I used System.Configuration.ConfigurationManager package to access configuration information.
Issue:
ConfigurationManager.GetSection(string) throws exception on .Net core platform whereas it works fine on Net472.
Error Message: Configuration system failed to initialize ---> System.Configuration.ConfigurationErrorsException: Unrecognized configuration section system.runtime.remoting
Work around tried so far:
ConfigurationManager.OpenExeConfiguration(configurationUserLevel).GetSection(string) works perfect on both the platforms for fetching the same section
Sample Code:
static MyConfigurationSection myConfigurationSettings { get; set; }
static void Main(string[] args)
{
LoadSettings();
}
private static void LoadSettings()
{
try
{
//Net472 : Works
//NetCoreApp2.1: Throws exception
myConfigurationSettings = ConfigurationManager.GetSection("myCustomSettings") as MyConfigurationSection;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
//Works on both platform
myConfigurationSettings = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).GetSection("myCustomSettings") as MyConfigurationSection;
Console.WriteLine(myConfigurationSettings.Applications.Count);
Console.ReadLine();
}
Here is configuration file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="myCustomSettings" type="TestConfigManager.MyConfigurationSection, TestConfigManager" />
</configSections>
<myCustomSettings>
<applications/>
</myCustomSettings>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="1111" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
Unfortunately, accessing configuration works slightly differently in the Core Framework (and also .NET 5 and 6). Even with the help of the links below, it took me some time to find it out.
This is how it worked for me:
As preparation, go to NUGET package manager and import
Microsoft.Extensions.Configation,
Microsoft.Extensions.Configation.Json,
Microsoft.Extensions.Configation.Xml
and (optional) Microsoft.Windows.Compatibility
Depending on the type of config file, access it as follows:
App.Config
Example:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="myKey" value="myValue"/>
</appSettings>
</configuration>
Declare
public static AppSettingsSection AppConfig { get; private set; } = null;
Initialize it via
AppConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
.AppSettings;
Read any keys via:
var myValue = AppConfig.Settings["myKey"].Value;
appconfig.json
Example:
{
"AppSettings": {
"myKey": "myValue"
}
}
Declare
public static IConfigurationSection JsonConfig { get; private set; } = null;
Initialize it via
JsonConfig = new ConfigurationBuilder().AddJsonFile("appconfig.json",
optional: true, reloadOnChange: true).Build().GetSection("AppSettings");
Read any keys via:
var myValue = JsonConfig["myKey"];
Helpful links:
cant read app config in c-sharp
how to read appsettings values from json
Comparision between appSettings and ApplicationSettings

Asp.net core 2 with angular 6 template

I am looking for a template to use asp.net core 2.0 with angular 6 in one solution with f5 hit to run the application.
Can you help find such request ?
Thanks
tutorial
here:
I would not use
Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0-rc1-final
but
Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0
You can use Angular6.
The magic is that .net core starts new command line itself and runs npm script.
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
npm start is by default alias for ng serve.
I do have working project with Angular6 and Core 2.
My project.csproj
...
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>ClientApp\</SpaRoot>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="2.0.0" />
</ItemGroup>
...
and Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.SpaServices.AngularCli;
namespace AngularSPA
{
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();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
// 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();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
}
But I only have aesthetic problem with built-in command line, you can see my question here.
I created one, it is on GitHub, it could be a good starting point but it is not perfect, it should grow and evolve during next months...
https://github.com/JuanGarciaCarmona/AspNetCore21Ang6Template
Also there is an article in Code Project that explains how to use it.
https://www.codeproject.com/Articles/1246748/Angular-within-ASP-NET-Core
I hope it helps.

how to know which framework using

When I am executing "dotnet new mvc -n MyWeb" command I am getting below code auto generated.
namespace MyWeb
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
Above code style is different than blog which I am following. The blog I am following shows below output. This is different than mine after executing same command. After googling, I knew it's due to difference in framework.
How I can know which framework in use?
How I can force Visual Studio to use ASP.Net core 1.0
namespace secondapp
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrol()
.UseContentRool(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UserStartup<Startup>()
.Build();
host.run()
}
}
}
Display .NET Core SDK version.
dotnet --version
Display .NET Core information.
dotnet --info
Create a new ASP.NET Core C# MVC application project in the current
directory with no authentication targeting .NET Core 1.0 (Specifies
the framework to target. Values: netcoreapp1.0 or netcoreapp1.1)
dotnet new mvc -au None -f netcoreapp1.0
Check the link for more details
https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new?tabs=netcore2x
Also from Visual Studio in Solution Explorer select your project and right click on it then select Properties, you will see the Target framework as below and you can change it.

.NET 2.0 Console App - Reading app settings

I am trying to get a .NET console application (Core 2.0) to read from an appsetting.json file. I have a webapi project that works fine with the following:
services.Configure<WebAppSettings>(Configuration.GetSection("WebAppSettings"));
But in the console app it says:
'ServiceCollection' does not contain a definition for 'Configure' and no extension method 'Configure' accepting a first argument of type 'ServiceCollection' could be found (are you missing a using directive or an assembly reference?)
I have the following packages installed in the console app project:
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
Here is the full Program.cs class:
using System;
using System.IO;
using Cam.Checker.Services;
using Cam.Common;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Cam.Checker
{
class Program
{
public static IConfigurationRoot Configuration { get; set; }
static void Main(string[] args)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
//.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);
Configuration = builder.Build();
var services = new ServiceCollection();
services.AddTransient<ICheckerService, CheckerService>();
// app settings
services.Configure<WebAppSettings>(Configuration.GetSection("WebAppSettings"));
var provider = services.BuildServiceProvider();
}
}
}
Thank you!
You need to add the NuGet package Microsoft.Extensions.Options.ConfigurationExtensions to get the Configure extension method.

Resources