dotnet core database first using NetTopologySuite - c#

I recently upgraded to the newest version of EntityFrameworkCore.PostgreSQL but the spacial data didn't seem to work, because they now use NetTopologySuite see here
To set up the NetTopologySuite plugin, add the
Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite nuget to your
project. Then, make the following modification to your UseNpgsql()
line:
I use the dotnet ef dbcontext scaffold command
dotnet ef dbcontext scaffold "MyConnectionString" Npgsql.EntityFrameworkCore.PostgreSQL
however, the scaffold command doesn't seem to use the NetTopologySuite mapping. I still get the following error
Could not find type mapping for column 'public.behaviour.coord' with data type 'geometry(Point)'. Skipping column.
How can I scaffold my database using NetTopologySuite

I had a similar problem, after updating the postgre library, I had to delete migration files and regenerate new ones

public class EFDesignTimeService : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
new EntityFrameworkRelationalServicesBuilder(services).TryAddProviderSpecificServices(x =>
{
x.TryAddSingleton<INpgsqlOptions, NpgsqlOptions>(p =>
{
var dbOption = new DbContextOptionsBuilder()
.UseNpgsql("connection string",
ob => ob.UseNodaTime().UseNetTopologySuite()).Options;
var npgOptions = new NpgsqlOptions();
npgOptions.Initialize(dbOption);
return npgOptions;
});
});
}
}

I was using the geometry(Point, 4326) type
and I had to change the type to geometry
ALTER COLUMN coord TYPE geometry;

Related

EF Core migration seems to ignore context option

Whilst adding Identity to my project I am doing so via a second context.
However, when running the following command
dotnet ef migrations add IdentityInitial -p .\DOH.Data -s .\DOH.API
-c AppIdentityDbContext -o DOH.Data\Identity\Migrations --verbose
I get the following output
Using assembly 'DOH.Data'.
Using startup assembly 'DOH.API'.
Using application base 'C:\Users\vic\Documents\projects\BugTracker\DOH\DOH.API\bin\Debug\netcoreapp3.1'.
Using working directory 'C:\Users\vic\Documents\projects\BugTracker\DOH\DOH.API'.
Using root namespace 'DOH.Data'.
Using project directory 'C:\Users\vic\Documents\projects\BugTracker\DOH\DOH.Data\'.
Remaining arguments: .
The Entity Framework tools version '5.0.1' is older than that of the runtime '5.0.5'. Update the tools for the latest features and bug fixes.
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'DOH.API'...
Finding Microsoft.Extensions.Hosting service provider...
Using environment 'Development'.
Using application service provider from Microsoft.Extensions.Hosting.
Found DbContext 'ApplicationContext'.
Found DbContext 'AppIdentityDbContext'.
Finding DbContext classes in the project...
Using context 'AppIdentityDbContext'.
System.InvalidOperationException: The entity type 'PersonApplicationRole' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943.
The entity PersonApplicationRole is an existing entity that is referenced in the ApplicationContext.
This entity has already been set up in the database via a previous migration using a composite key, so I know that part works
If I change the entity definition to cope with the error just to get through the Identity migration, I end up with a migration that has all of the original entities referenced from ApplicationContext as well as the tables that need would be created as part of the Identity set up.
So this feels like the -c option is being ignored.
This is the definition of ApplicationContext:
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<ApplicationContext>
{
public ApplicationContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(#Directory.GetCurrentDirectory() + "/../DOH.API/appsettings.Development.json").Build();
var builder = new DbContextOptionsBuilder<ApplicationContext>();
var connectionString = configuration.GetConnectionString("doh.dev");
builder.UseNpgsql(connectionString);
return new ApplicationContext(builder.Options);
}
}
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options)
{ }
}
This is the current AppIdentityDbContext:
public class AppIdentityDbContext : IdentityDbContext<AppUser>
{
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
These are the two connection strings
"ConnectionStrings": {
"doh.dev": "Host=x.x.x.x;Port=5432;Username=dohdbsvc;Password=******;Database=dohdb;",
"doh.dev.identity": "Host=x.x.x.x;Port=5432;Username=dohdbsvc;Password=******;Database=doh.identitydb;"
},
And here are the two contexts as included in my startup
services.AddDbContext<ApplicationContext>(options =>
{
options.EnableDetailedErrors();
options.UseNpgsql(
Configuration.GetConnectionString("doh.dev"));
});
services.AddDbContext<AppIdentityDbContext>(options =>
{
options.EnableDetailedErrors();
options.UseNpgsql(
Configuration.GetConnectionString("doh.dev.identity"));
});
For reference, this is a .net core 3.1 project using the following packages
Microsoft.AspNetCore.Identity.EntityFrameworkCore 5.0.5
Microsoft.AspNetCore.Identity 2.2.0
Microsoft.EntityFrameworkCore 5.0.5
Microsoft.EntityFrameworkCore.Design 5.0.5
Npgsql.EntityFrameworkCore.PostgreSQL 5.0.2
Microsoft.EntityFrameworkCore.Tools 5.0.5
I have been through the docs and countless examples others have posted on various blogs and forums and I appear to be following recommended practice, so if anyone can see why this is happening, please share your thoughts.

Xamarin.Forms doesn't find migrations of EntityFramework.Core

I Am wondering what i am doing wrong because EnsureCreated() works as i want but Migrate() doesn't work because no migrations are ever found.
Here is some code:
public SqliteContext(string FileName)
{
this.FileName = FileName;
Database.EnsureCreated();
}
Works
But when i do the same but with:
public SqliteContext(string FileName)
{
this.FileName = FileName;
Database.Migrate();
}
It doesn't. So i checked and it seems no migrations are ever found. But i have my migrations in the same project as my SqliteContext/dbContext. (In map Migrations) So i assume i don't have to specify a migration assembly but i did that as well but it still didn't work.
For some extra information:
My migrations are generated by Microsoft.EntityFrameworkCore.Tools.
I am using:
Microsoft.EntityFrameworkCore.Tools 3.1.12
Microsoft.EntityFrameworkCore.Sqlite 3.1.12
Xamarin.Forms 5.0.0.2012
Xamarin.Essentials 1.6.1
FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "narco.db"
Seems i found the solution.
public class DbMigrationContext : SqliteContext
{
public DbContext() : base(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "narco.db"))
{
}
}
I added this class to my Core project so if there were any changes in my context i use in my project they auto reflect in the context i use to create my migrations.
[DbContext(typeof(DbMigrationContext))]
Is put on top of my migration so its not found. So when i changed it to
[DbContext(typeof(SqliteContext))]
It now works as expected. So i guess from now on i will just disconnect the reference to my project with my SqliteContext and just copy it over to avoid issues like this.
Hopefully whoever has this issue will find this useful.

Scaffold-DbContext SQL Views ? ASP NET CORE 3.1

I need your help.
I read on the web that there has never been a proper way to import views from sql into our asp.net core project.
Do you know if in version 3.1 you can do ? If so, how?
For tables I use the "scaffold-DbContext" command.
Thank you very much!
Although you cannot use scaffold-DbContext for database view but you can still use SQL View in your .Net Core project.
Suppose you have the following SQL View
CREATE VIEW [dbo].[v_MyTableResult] AS
SELECT
SELECT Id, Name FROM MyTable
GO
Create a new Model class based on the result set of SQL View.
public class MyTableModel
{
public int Id { get; set; }
public string Name { get; set; }
}
In the database context class, introduce property for the Model.
public virtual DbSet<MyTableModel> MyTableResults { get; set; }
In OnModelCreating method, configure model properties
modelBuilder.Entity<MyTableModel>(entity =>
{
entity.HasNoKey();
entity.Property(e => e.Id)
.HasColumnName("Id");
entity.Property(e => e.Name)
.HasColumnName("Name");
});
Finally, in the method where you need results from the view, call FromSqlRaw on DbSet property
var myTableResults =
this.MyDbContext.MyTableResults.FromSqlRaw(
"SELECT * FROM dbo.v_MyTableResult").ToList();
It is possible to scaffold a view. Just use -Tables the way you would to scaffold a table, only use the name of your view. E.g., If the name of your view is ‘vw_inventory’, then run this command in the Package Manager Console (substituting your own information for "My..."):
PM> Scaffold-DbContext "Server=MyServer;Database=MyDatabase;user id=MyUserId;password=MyPassword" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Temp -Tables vw_inventory
This command will create a model file and context file in the Temp directory of your project. You can move the model file into your models directory (remember to change the namespace name). You can copy what you need from the context file and paste it into the appropriate existing context file in your project.
Note: If you want to use your view in an integration test using a local db, you'll need to create the view as part of your db setup. If you’re going to use the view in more than one test, make sure to add a check for existence of the view. In this case, since the SQL ‘Create View’ statement is required to be the only statement in the batch, you’ll need to run the create view as dynamic Sql within the existence check statement. Alternatively you could run separate ‘if exists drop view…’, then ‘create view’ statements, but if multiple tests are running concurrently, you don’t want the view to be dropped if another test is using it.
Example:
void setupDb() {
...
SomeDb.Command(db => db.Database.ExecuteSqlRaw(CreateInventoryView()));
...
}
public string CreateInventoryView() => #"
IF OBJECT_ID('[dbo].[vw_inventory]') IS NULL
BEGIN EXEC('CREATE VIEW [dbo].[vw_inventory] AS
SELECT ...')
END";
This is a helpful link. It describes adding the code sections by hand (as Nouman mentioned) instead of scaffolding them: https://learn.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=fluent-api
It seems not supported yet. For a workaround, you could refer to
Is it possible to automatically map a DB view on Entity Framework Core version 2.1?
Yes, we can use scaffold-DbContext with view.
using scaffold-DbContext for database view (myview). I executed below scaffold-DbContext query.
Scaffold-DbContext "Data Source=my_server,1433;Initial Catalog=my_database;User Id=my_user;Password=my_password;" Microsoft.EntityFrameworkCore.SqlServer -o Entities -Context ContextName -t myview -f
Once you execute then you will get entity class and contextfile.
Code in entity class (myview.cs)
////// Fetching data
public partial class myview
{
public Guid property_1 { get; set; }
}
Code in dbcontect file (ContextName.cs)
public partial class ContextName : DbContext
{
public virtual DbSet<myview> myview { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<myview>(entity =>
{
entity.HasNoKey();
entity.ToView("myview", "ExtIntDWFS");
////// other property configuration.
}
}
}
Code in controller to fetch view data
////// Fetching data
public async Task<myview> GetView(Base #base)
{
myview obj = new myview();
using (ContextName context = new ContextName())
{
obj = await context.myview.Select(s => new myview
{
property_1 = s.property_1,
}).FirstOrDefaultAsync();
}
return obj;
}
Above code is working for me successfully.
I recently had a similar need (retrieve one ore more views and create the corresponding classes in a .NET project). It can be done with Scaffold-DbContect, for example:
Scaffold-DbContext "Server=<address>;
Database=<dbname>;
Trusted_Connection=True"
Microsoft.EntityFrameworkCore.SqlServer -OutputDir
ModelDirectory -t <table_name> -t <view_name>
It creates a folder called OutputDir with the corresponding classes for the indicated tables or views
My solution was similar to Jainith's solution, but the trick for me was to make sure the solution compiled successfully before I ran the Scaffold command. Using .net core 6 and EF 6
I then ran my command:
Scaffold-DbContext "Server=MyServer;Database=myDb;user=theUser;password=thePwd;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entities
-ContextDir . -Context MyContext -UseDatabaseNames -Force -NoPluralize -NoOnConfiguring -Tables vwMyView
The command ran successfully and created my class in the root folder. I then moved it into my Models folder and went from there. That is optional of course.
There is more info here as to why you need to make sure the solution compiles before you run the scaffold command.

EF .Net core 2.0 - Using OwnOne with HasDefaultValueSql for Id (type: Guid)

I have 2 Entities. Order and Address (as Value Object in DDD).
I want Id Property of Order to be generated by Guid.NewGuid(). I've configured as the following
class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> orderConfiguration)
{
orderConfiguration.ToTable("orders", OrderingContext.DEFAULT_SCHEMA);
orderConfiguration.HasKey(o => o.Id);
orderConfiguration.Ignore(b => b.DomainEvents);
orderConfiguration.Property(b => b.Id).HasDefaultValueSql<Guid>("(newid())");
//Address value object persisted as owned entity type supported since EF Core 2.0
orderConfiguration.OwnsOne(o => o.Address);
}
}
But, when using command "dotnet ef migrations add InitialDatabase" for creating migration, which throw error " 'Order.Id' and 'Order.Address#Address.OrderId' are both mapped to column 'Id' in 'ordering.orders' but are configured to use different default values ('(newid())' and '')."
Please, help me. I'm new in .net Core2, EF core as well. Thank you.

Add migration with different assembly

I am working on a project with ASP.NET CORE 1.0.0 and I am using EntityFrameworkCore. I have separate assemblies and my project structure looks like this:
ProjectSolution
-src
-1 Domain
-Project.Data
-2 Api
-Project.Api
In my Project.Api is the Startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ProjectDbContext>();
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ProjectDbContext>()
.AddDefaultTokenProviders();
}
The DbContext is in my Project.Data project
public class ProjectDbContext : IdentityDbContext<IdentityUser>
{
public ProjectDbContext(DbContextOptions<ProjectDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var builder = new ConfigurationBuilder();
builder.SetBasePath(Directory.GetCurrentDirectory());
builder.AddJsonFile("appsettings.json");
IConfiguration Configuration = builder.Build();
optionsBuilder.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"));
base.OnConfiguring(optionsBuilder);
}
}
When I try to make the initial migration, I get this error:
"Your target project 'Project.Api' doesn't match your migrations assembly 'Project.Data'. Either change your target project or change your migrations assembly.
Change your migrations assembly by using DbContextOptionsBuilder. E.g. options.UseSqlServer(connection, b => b.MigrationsAssembly("Project.Api")). By default, the migrations assembly is the assembly containing the DbContext.
Change your target project to the migrations project by using the Package Manager Console's Default project drop-down list, or by executing "dotnet ef" from the directory containing the migrations project."
After I seeing this error, I tried to execute this command located in Project.Api:
dotnet ef --startup-project ../Project.Api --assembly "../../1 Data/Project.Data" migrations add Initial
and I got this error:
"Unexpected value '../../1 Domain/Project.Data' for option 'assembly'"
I don't know why I get this error, when I try to execute the command with the '-assembly' parameter.
I can't create a Initial Migration from other assembly and I've searched for information about it but didn't got any results.
Has someone had similar issues?
All EF commands have this check:
if (targetAssembly != migrationsAssembly)
throw MigrationsAssemblyMismatchError;
targetAssembly = the target project you are operating on. On the command line, it is the project in the current working directory. In Package Manager Console, it is whatever project is selected in the drop down box on the top right of that window pane.
migrationsAssembly = assembly containing code for migrations. This is configurable. By default, this will be the assembly containing the DbContext, in your case, Project.Data.dll.
As the error message suggests, you have have a two options to resolve this
1 - Change target assembly.
cd Project.Data/
dotnet ef --startup-project ../Project.Api/ migrations add Initial
// code doesn't use .MigrationsAssembly...just rely on the default
options.UseSqlServer(connection)
2 - Change the migrations assembly.
cd Project.Api/
dotnet ef migrations add Initial
// change the default migrations assembly
options.UseSqlServer(connection, b => b.MigrationsAssembly("Project.Api"))
I had the same problem until I noticed that on the package manager console top bar => "Default Projects" was supposed to be "Project.Data" and not "Project.API".
Once you target the "Project.Data" from the dropdown list and run the migration you should be fine.
Using EF Core 2, you can easily separate your Web project from your Data (DbContext) project. In fact, you just need to implement the IDesignTimeDbContextFactory interface. According to Microsoft docs, IDesignTimeDbContextFactory is:
A factory for creating derived DbContext instances. Implement this
interface to enable design-time services for context types that do not
have a public default constructor. At design-time, derived DbContext
instances can be created in order to enable specific design-time
experiences such as Migrations. Design-time services will
automatically discover implementations of this interface that are in
the startup assembly or the same assembly as the derived context.
In the bottom code snippet you can see my implementation of DbContextFactory which is defined inside my Data project:
public class DbContextFactory : IDesignTimeDbContextFactory<KuchidDbContext>
{
public KuchidDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbContextBuilder = new DbContextOptionsBuilder<KuchidDbContext>();
var connectionString = configuration.GetConnectionString("Kuchid");
dbContextBuilder.UseSqlServer(connectionString);
return new KuchidDbContext(dbContextBuilder.Options);
}
}
Now, I can initialize EF migration by setting my Web project as the StartUp project and selecting my Data project inside the Package Manager Console.
Add-Migration initial
You can find more details here. However, this blog post uses an obsoleted class instead of IDesignTimeDbContextFactory.
Add Migration With CLI Command:
dotnet ef migrations add NewMigration --project YourAssemblyName
Add Migration With PMC Command:
Add-Migration NewMigration -Project YourAssemblyName
Link About CLI Commands
Link About PMC Commands
I ran on the same problem and found this
We’re you trying to run your migrations on a class library? So was I. Turns out this isn’t supported yet, so we’ll need to work around it.
EDIT: I found solution on this git repo
Currently I think EF only supports to add migrations on projects not yet on class libraries.
And just side note for anybody else who wants to add migrations to specific folder inside your project:
EF CLI not support this yet. I tried --data-dir but it didn't work.
The only thing works is to use Package Manager Console:
Pick your default project
use -OutputDir command parameter, .e.g., Add-Migration InitConfigurationStore -OutputDir PersistedStores/ConfigurationStore command will output the mgiration to the folder 'PersistedStores/ConfigurationStore' in my project.
Updates as of 10/12/2017
public void ConfigureServices(IServiceCollection services)
{
...
string dbConnectionString = services.GetConnectionString("YOUR_PROJECT_CONNECTION");
string assemblyName = typeof(ProjectDbContext).Namespace;
services.AddDbContext<ProjectDbContext>(options =>
options.UseSqlServer(dbConnectionString,
optionsBuilder =>
optionsBuilder.MigrationsAssembly(assemblyName)
)
);
...
}
Updates as of 1/4/2021
I am using EF Core 5.0 this time. I was hoping optionBuilder.MigrationAssembly() method would work when you want to generate migrations under a folder in the target project but it didn't.
The structure I have this time is:
src
- presentation
- WebUI
- boundedContext
- domain
- application
- infrastructure
- data/
- appDbContext
- email-services
- sms-services
See I have the infrastructure as a class library, and it contains multiple folders because I want to just have a single project to contain all infrastructure related services. Yet I would like to use folders to organize them.
string assemblyName = typeof(ProjectDbContext).Namespace would give me the correct path "src/infrastructure/data", but doing add-migration still fails because that folder is not an assembly!
Could not load file or assembly. The system cannot find the file
specified.
So the only thing that actually works is, again, to specify the output folder...
Using .NET Core CLI you would have to open the command line under your target project, and do the following:
dotnet ef migrations add Init
-o Data\Migrations
-s RELATIVE_PATH_TO_STARTUP_PROJECT
Directory Structure
Root
APIProject
InfrastructureProject
By going Root directory
To add migration
dotnet ef migrations add Init --project InfrastructureProject -s APIProject
To update database
dotnet ef database update --project InfrastructureProject -s APIProject
(ASP.NET Core 2+)
Had the same issue.
Here is what I did:
Reference the project that contains the DbContext (Project.A) from the project that will contain the migrations (Project.B).
Move the existing migrations from Project.A to Project.B
(If you don't have migrations - create them first)
Configure the migrations assembly inside Project.A
options.UseSqlServer(
connectionString,
x => x.MigrationsAssembly("Project.B"));
Assuming your projects reside in the same parent folder:
dotnet ef migrations add Init --p Project.B -c DbContext
The migrations now go to Project.B
Source: Microsoft
dotnet ef update-database --startup-project Web --project Data
Web is my startup project
Data is my the my class library
There are multiple projects included in the Solution.
Solution
|- MyApp (Startup Proj)
|- MyApp.Migrations (ClassLibrary)
Add-Migration NewMigration -Project MyApp.Migrations
Note: MyApp.Migrations also includes the DbContext.
The below command did the trick for me. I'm using VS Code and I run the following command:
SocialApp.Models> dotnet ef migrations add InitialMigartion --startup-project ../SocialApp.API
Courtesy: https://github.com/bricelam/Sample-SplitMigrations
This is for EF Core 3.x.
Based on this answer from Ehsan Mirsaeedi and this comment from Ales Potocnik Hahonina, I managed to make Add-Migration work too.
I use Identity Server 4 as a NuGet package and it has two DB contexts in the package.
Here is the code for the class that implements the IDesignTimeDbContextFactory interface:
public class PersistedGrantDbContextFactory : IDesignTimeDbContextFactory<PersistedGrantDbContext>
{
public PersistedGrantDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbContextBuilder = new DbContextOptionsBuilder<PersistedGrantDbContext>();
var connectionString = configuration.GetConnectionString("db");
dbContextBuilder.UseSqlServer(connectionString, b => b.MigrationsAssembly("DataSeeder"));
return new PersistedGrantDbContext(dbContextBuilder.Options, new OperationalStoreOptions() { ConfigureDbContext = b => b.UseSqlServer(connectionString) });
}
}
Compared to the answer of Ehsan Mirsaeedi I modified these:
I added the MigrationsAssembly:
dbContextBuilder.UseSqlServer(connectionString, b => b.MigrationsAssembly("DataSeeder"));
Where the "DataSeeder" is the name of my startup project for seeding and for migrations.
I added an options object with ConfigureDbContext property set to the connection string:
return new PersistedGrantDbContext(dbContextBuilder.Options, new OperationalStoreOptions() { ConfigureDbContext = b => b.UseSqlServer(connectionString) });
It is now usable like this:
'Add-Migration -Context PersistedGrantDbContext
At this point, when a migration has been created, one can create a service for this in a migration project having a method like this:
public async Task DoFullMigrationAsync()
{
using (var scope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var persistedGrantDbContextFactory = new PersistedGrantDbContextFactory();
PersistedGrantDbContext persistedGrantDbContext = persistedGrantDbContextFactory.CreateDbContext(null);
await persistedGrantDbContext.Database.MigrateAsync();
// Additional migrations
...
}
}
I hope I helped someone.
Cheers,
Tom
All you have to do, is modify your ConfigureServices like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ProjectDbContext>(item => item.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly("Project.Api")));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ProjectDbContext>()
.AddDefaultTokenProviders();
}
By Default VS will use the Assembly of the project where the DbContext is stored. The above change, just tells VS to use the assembly of your API project.
You will still need to set your API project as the default startup project, by right clicking it in the solution explorer and selecting Set as Startup Project
Mine is a single .net core web project.
Had to ensure 1 thing to resolve this error. The following class must be present in the project.
public class SqlServerContextFactory : IDesignTimeDbContextFactory<SqlServerContext>
{
public SqlServerContext CreateDbContext(string[] args)
{
var currentEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{ currentEnv ?? "Production"}.json", optional: true)
.Build();
var connectionString = configuration.GetConnectionString("MsSqlServerDb");
var optionsBuilder = new DbContextOptionsBuilder<SqlServerContext>();
//var migrationAssembly = typeof(SqlServerContext).Assembly.FullName;
var migrationAssembly = this.GetType().Assembly.FullName;
if (connectionString == null)
throw new InvalidOperationException("Set the EF_CONNECTIONSTRING environment variable to a valid SQL Server connection string. E.g. SET EF_CONNECTIONSTRING=Server=localhost;Database=Elsa;User=sa;Password=Secret_password123!;");
optionsBuilder.UseSqlServer(
connectionString,
x => x.MigrationsAssembly(migrationAssembly)
);
return new SqlServerContext(optionsBuilder.Options);
}
}
Note there the migration assembly name.
//var migrationAssembly = typeof(SqlServerContext).Assembly.FullName;
I have commented that out. That is the culprit in my case. What is needed is the following.
var migrationAssembly = this.GetType().Assembly.FullName;
With that in place the following two commands worked perfectly well.
Add-Migration -StartupProject MxWork.Elsa.WebSqLite -Context "SqlServerContext" InitialMigration
Add-Migration InitialMigration -o SqlServerMigrations -Context SqlServerContext
If you want a reference of such a project, take a look at this git hub link
There you should find a project attached with the name Elsa.Guides.Dashboard.WebApp50.zip. Download that see that web app.
I was facing similar issue, though answers seems straight forward somehow they didn't work.
My Answer is similar to #Ehsan Mirsaeedi, with small change in DbContextFactory class. Instead of Adding migration assembly name in Startup class of API, I have mentioned in DbContextFactory class which is part of Data project(class library).
public class DbContextFactory : IDesignTimeDbContextFactory<KuchidDbContext>
{
public KuchidDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbContextBuilder = new DbContextOptionsBuilder<KuchidDbContext>();
var connectionString = configuration.GetConnectionString("connectionString");
var migrationAssemblyName= configuration.GetConnectionString("migrationAssemblyName");
dbContextBuilder.UseSqlServer(connectionString, o => o.MigrationAssembly(migrationAssemblyName));
return new KuchidDbContext(dbContextBuilder.Options);
}
}
You would need 'Microsoft.Extensions.Configuration' and 'Microsoft.Extensions.Configuration.Json' for SetBasePath & AddJsonFile extensions to work.
Note: I feel this is just a work around. It should pickup the DbContextOptions from the startup class somehow it is not. I guess there is definitely some wiring issue.
If you have solution with few projects, where
API - startup here
EF - db context here
then to perform migration:
install Microsoft.EntityFrameworkCore.Tools for API
open Package Manager Console in Visual Studio
perform Add-Migration InitialCreate
notice that "DefaultProject: EF" should be selected in the console.
I have resolved it by adding below line in Startup.cs. Hope it will help you also. I have used Postgres you can use Sql Server instead of that
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddSigningCredential(cert)
.AddCustomUserStore<IdentityServerConfigurationDbContext>()
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
});
temporary rename docker proj file, solve on my issue
For all of you who have multiple startup projects.
Notice that you need to set your target project as startup project - Project.Api(form the question example) should be the startup project.
Hope that will help someone :)

Categories