ApplicationDbContext.OnModelCreating() in .Net Standard 2.0 has MissingMethodException - c#

this is one of the problems that I issued in "Found conflicts between different versions" OR System.MissingMethodException when using aspnetcore.identity, but I wanted to explain it separately with a small example:
SetUp:
A .Net Standard 2.0 Library
A .Net Framework 4.7.2 WebForms application
The .Net standard Library has this Code:
public class Class1
{
public static void doStuff()
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer("MyConnectionString");
var userStore = new UserStore<ApplicationUser, MyRole, ApplicationDbContext, Guid>(
new ApplicationDbContext(optionsBuilder.Options)
); // the userStore.Users property shows already the exception in the debugger
AspNetUserManager<ApplicationUser> userManager = new AspNetUserManager<ApplicationUser>(
userStore,
null,
new PasswordHasher<ApplicationUser>(),
new List<UserValidator<ApplicationUser>>() { new UserValidator<ApplicationUser>() },
null,
null,
null,
null,
null
);
var x = userManager.Users; // exception throw (see next code snipped)
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, MyRole, Guid>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder); // after userManager.Users is called the exception is thrown here
}
}
And in the WebForms-Project just:
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
Class1.doStuff();
}
}
Exception:
System.MissingMethodException: 'Methode nicht gefunden: "Microsoft.EntityFrameworkCore.Metadata.Builders.IndexBuilder Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder1.HasIndex(System.Linq.Expressions.Expression1>)".'
Configuration of .Net Standard 2.0 Project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.1" />
</ItemGroup>
</Project>
References:
https://github.com/dotnet/aspnetcore/issues/8467 Here they say to update to "Microsoft.AspNetCore.Identity" 3.x, but I cannot do that because its not compatible with the .Net Standard 2.0 project and I need that for the WebForms Project.
So my question would be if someone knows a workaround to generate an Usermanager in a different way... I need to Get and Set Users (and validate) using aspnetcore-identity (database etc. is already migrated)

So, I have a solution for my problem:
use Microsoft.EntityFrameworkCore.SqlServer version 2.2.6 (NOT 3.1.1 even though its supposed to work with .net standard 2.0...)
And a sequence of
deleting nuget packages
clearing nuget cache
deleting nuget packages on the local computer (in the C:/../Users/.../.nuget folder)
changing the nuget management format to "PackageReference"
cry a lot
reinstalling everything and dealing with warnings helped
(I cant say what exactly the issue was regarding the version-conflicts)

Related

Customizing a Migration File

this is my very first post.
Does anyone know how to add text to an EF Migration File?
Specifically, I would like to add this text:
var sqlFile = Path.Combine("Scripts/CustomScript.Sql");
migrationBuilder.Sql(File.ReadAllText(sqlFile));
to the bottom of:
public partial class MyMigration : Migration
protected override void Up(MigrationBuilder migrationBuilder)
protected override void Down(MigrationBuilder migrationBuilder)
in all migration files all the time
I don't mean by editing the generated file but by some dynamic mechanism so that there are no extra steps in performing this action: "dotnet ef database update --context MyAppContext"
<TargetFramework>net6.0</TargetFramework>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.5" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
(I am using the latest (11.05.2022) VS2022 console template with .NET6 syntax)
I have tried all the suggestions in this post but none work in the newest .NET code versions.
I am new to StackOverflow and appreciate any and all positive feedback. TY
Since it's a partial class, you can create a new partial class in a different file or directory with the same class name and create a method with the definition that you want to append in the migration file.
partial class MyMigration{
void MigrationContent(){
var sqlFile = Path.Combine("Scripts/CustomScript.Sql");
migrationBuilder.Sql(File.ReadAllText(sqlFile));
}
Call the Method inside the MyMigration class after where you want to append it.
public partial class MyMigration : Migration
protected override void Up(MigrationBuilder migrationBuilder){}
protected override void Down(MigrationBuilder migrationBuilder){}
MigrationContent();

Problems with user secrets in .Net console apps

I've created a .Net 6 console app. I added user secrets, but I only get the values defined in the appsettings.json file. I use Visual Studio Professional 2022 version 17.0.4.
Initial steps
Create a new .Net 6 console app from Visual Studio 2022's project templates.
Install the Microsoft.Extensions.Hosting nuget package (version 6.0.0).
Install the Microsoft.Extensions.Configuration.UserSecrets nuget package (version 6.0.0).
Add the appsettings.json file and set Copy to Output Directory to Copy always.
Right-click on the project and select Manage User Secrets.
Code
Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.Configure<GlobalSettings>(context.Configuration.GetSection("GlobalSettings"));
services.AddTransient<Worker>();
})
.Build();
var work = host.Services.GetRequiredService<Worker>();
await work.ExecuteAsync();
Worker.cs
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
public class Worker
{
private readonly ILogger<Worker> _logger;
private readonly GlobalSettings _globalSettings;
public Worker(ILogger<Worker> logger, IOptions<GlobalSettings> globalSettings)
{
_logger = logger;
_globalSettings = globalSettings.Value;
}
public async Task ExecuteAsync()
{
_logger.LogInformation(_globalSettings.Foo);
_logger.LogInformation(_globalSettings.Bar);
await Task.CompletedTask;
}
}
GlobalSettings.cs:
public class GlobalSettings
{
public string Foo { get; set; }
public string Bar { get; set; }
}
.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<UserSecretsId>deedd476-f5d6-47f4-982e-1645c89789c7</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
appsettings.json:
{
"GlobalSettings": {
"Foo": "Normal Foo",
"Bar": "Normal Bar"
}
}
secrets.json:
{
"GlobalSettings": {
"Foo": "Secret Foo",
"Bar": "Secret Bar"
}
}
What I've tried
Checked whether the user secret id in csproj matched the folder, which it did: C:\Users\Dennis\AppData\Roaming\Microsoft\UserSecrets\deedd476-f5d6-47f4-982e-1645c89789c7
Rebuilded the project.
Closed Visual Studio 2022 and ran it again as administrator.
Created a new project from scratch.
Compared the code with one of my colleague's projects. I couldn't find any difference, but his code works when I run it.
Changed IOptionsSnapshot to IOptions and IOptionsMonitor.
Changed AddTransient to AddSingleton.
Did the same steps, but with a .Net 5 project instead.
Thanks.
I'd misunderstood the way the Host.CreateDefaultBuilder method worked. According to the documentation (docs):
load app IConfiguration from User Secrets when EnvironmentName is 'Development' using the entry assembly
My environment is Production (probably a fallback value). It worked in my colleague's project because it included a launchSettings.json file with the DOTNET_ENVIRONMENT environment variable set to Development.

Problems in dependency/reference setup within a solution (assembly not found / file not found)

I have problems setting up my dependencies within a solution.
The following solution structure:
LibraryProjectA (.netstandard 2.0) has a NuGet reference to "system.componentmodel.annotations" /
LibraryProjectB (.netstandard 2.0) references LibraryProjectA
ConsoleProject (.NET Framework 4.8) references LibraryProjectA and LibraryProjectB
LibraryProjectA has this base class:
public abstract class FooConverterBase<T> : IFooConverter<T> {
public T Convert(object input) {
var result = DoConvert(input);
ValidateResult(result);
return result;
}
protected abstract T DoConvert(object input);
private void ValidateResult(T result) {
var validationContext = new ValidationContext(result);
Validator.ValidateObject(result, validationContext, true);
}
}
LibraryB extends that base class public class BarConverter : FooConverterBase<MyObject> but when I try to use BarConverter within the ConsoleProject I get an assembly not found / file not found exception. I resolve this issue when I install "system.componentmodel.annotations" directly as a NuGet package to my ConsoleProject.
How do I have to setup my dependecies or LibraryProjectA.csproj file that I won't have to manually install that package to my ConsoleProject?
Current LibraryProjectA.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>
</Project>

.NET Core 3.1 and SQL Server hierarchyid - multiple exceptions

I work with SQL Server Db in my .Net Core 3.1 project and some stored procedures and views have hierarchyid types for parameters and data.
I use Microsoft.Data.SqlClient package. And when I try to read data with SqlDataReader I get the exception:
System.IO.FileNotFoundException : Could not load file or assembly 'Microsoft.SqlServer.Types, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'. The system cannot find the file specified.
ok, I tried to use Microsoft.SqlServer.Types as it suggests but this package is not .NET Standard and it doesn't work.
Also, I found EntityFrameworkCore.SqlServer.HierarchyId but when I use it I get:
System.InvalidCastException : Unable to cast object of type 'Microsoft.SqlServer.Types.SqlHierarchyId' to type 'Microsoft.Data.SqlClient.Server.IBinarySerialize'.
So how on Earth can one use HierarchyId type in .NET Core 3.1?
I'm planning to host this solution on linux.
UPDATE
I do use Microsoft.Data.SqlClient 2.0 which is compatible with .NET Core. Also, I added then EntityFrameworkCore.SqlServer.HierarchyId, and I get this error:
System.InvalidCastException : Unable to cast object of type 'Microsoft.SqlServer.Types.SqlHierarchyId' to type 'Microsoft.Data.SqlClient.Server.IBinarySerialize'.
Here's the .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EntityFrameworkCore.SqlServer.HierarchyId" Version="1.1.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<Folder Include="Helpers\" />
</ItemGroup>
</Project>
No luck so far.
UPDATE 2
This is the code where the exception is thrown:
using (SqlDataReader reader = await detailsCmd.ExecuteReaderAsync())
{
while (reader.Read())
{
details.Add(new HierarchyDetails
{
Id = reader.GetInt32(0),
groupPath = reader.GetValue(1).ToString(), // <==== EXCEPTION
name = reader.GetString(2),
optionalData = reader.IsDBNull(3) ? null : reader.GetString(3)
});
}
}
And the table has the only row:
id groupPath culture name optionalData
24 0x58 en-US testing
Your error message suggests that you use something which uses nuget https://www.nuget.org/packages/Microsoft.SqlServer.Types/10.50.1600.1 which is .NET Framework dll, not .NET Core.
You mention that you use Microsoft.Data.SqlClient, please ensure that you use https://www.nuget.org/packages/Microsoft.Data.SqlClient/ which is compatible with .NET Core.
In case of other errors please check also Entity Framework Core hierarchyid
The solution at the moment to cast hierarchyid to NVARCHAR in all your queries:
... CAST(groupPath as NVARCHAR(4000)) as groupPath ...
and then use it as string.

EF-Core Single Deployment; Multiple Databases; Migrations

I currently have a .net core web api that has a SQL Server database per client. An api key is required to be passed in for every call and then is looked up in a master tenant database to get the correct connection string. The api will then set the connection string in the startup file and run the call.
Within the api, I have an endpoint that allows me to update all tenants to the latest migration and I also have a console app that will do the same. Something like this:
public async Task UpdateAllDatabases()
{
var qry = await GetAll(null, true);
foreach (var i in qry)
{
var optionsBuilder = new DbContextOptionsBuilder<MigrationContext>()
.UseSqlServer(i.DatabaseConnectionString);
using (var tenantContext = new MigrationContext(optionsBuilder.Options, _appSettings))
{
tenantContext.Database.Migrate();
}
}
}
The issue I am having is when I need to remove-migration. How can I remove a migration from all tenant databases?
You can use the same Migrate method but with the parameter ‘targetMigration’.
This will upgrade or rollback all databases to the specified target migration.
public void Migrate (string targetMigration = null);
(https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.migrations.imigrator.migrate)
Update: added example
MigrationContext.cs
public class MigrationContext : DbContext
{
}
Execute migrations
using (var tenantContext = new MigrationContext())
{
tenantContext.Database.GetService<IMigrator>().Migrate("targetMigration");
}
.csproj file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.4" />
</ItemGroup>
</Project>
i'm resloved
EFCoreMigrateMultiDatabase
this is my demo,need replace service IMigrationsScaffolder and IMigrationsAssembly support different database with single dbcontext to migration

Categories

Resources