Reputation: 3715
This seem to be an issue that have been fixed already, at least for the SQLite databases.
My solution consists of 3 projects:
I have installed the following packages into the main WPF project:
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.Design
Projects 2 and 3 are referenced in my main WPF project. So basically, it should be enough for the EF to resolve the DbContextes.
However, it's not - as running Add-Migration
on my WPF project results in:
PM> Add-Migration "Initial"
No DbContext was found in assembly 'TestWPFProject'. Ensure that you're using the correct assembly and that the type is neither abstract nor generic.
Switching to project 3
as default in the Package Manager Console results in:
PM> Add-Migration "Initial"
Unable to create an object of type 'ClientDbContext'. Add an implementation of 'IDesignTimeDbContextFactory<ClientDataStoreDbContext>' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.
How can I properly use EF Core migrations with my class library project and WPF project?
Upvotes: 23
Views: 38930
Reputation: 1400
I was using class library project as data layer and asp.net core project as start up project.First I registered my E.F core context in asp.net start up class.
public void ConfigureServices(IServiceCollection services)
{
//DataLayer namespace is in class library project
services.AddDbContext<DataLayer.EFCoreContext>();
}
Then in my context class(located in class library project) I provided connection string from setting file and used it in OnConfiguring method.
public class EFCoreContext: DbContext
{
private string ConnectionString { get; set; }
public EFCoreContext() : base()
{
var settingsPath= AppDomain.CurrentDomain.BaseDirectory;
settingsPath += @"\datalayersettings.json";
var datalayersettings =File.ReadAllText(settingsPath);
dynamic jSetting = JObject.Parse(datalayersettings);
this.ConnectionString =
(string)jSetting.ConnectionStrings.ConnectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder
optionsBuilder)
{
optionsBuilder.UseSqlServer(this.ConnectionString);
}
}
then for applying migration and updating database I used following commands respectively
add-migration MyEFCoreMigration -Context DataLayer.EFCoreContext -Project DataLayer
Update-Database -Context DataLayer.EFCoreContext -Project DataLayer
Upvotes: 2
Reputation: 343
What worked for me is just do your regular stuff on your UI Side Startup.cs
services.AddDbContext<ShentonEvaDbContext>(options =>
options.UseSqlServer(
_configuration.GetConnectionString("DevelopmentConnection")));
Then on your DBContext Configuration just add a constructor
public ShentonEvaDbContext(DbContextOptions<ShentonEvaDbContext> options) : base(options)
{
}
After that on your Package Manager Console just run the following
dotnet ef migrations add InitialMigration --project "NameOfDbContextProject" --startup-project "NameOfWebUIProject"
Then Everytihng went added and do the same for the update of database
Upvotes: 16
Reputation: 1301
I reproduced your solution and found... a solution :)
Let's get to deepen.
Name: ClassLibrary1.
Type: .NET Standard 2.0 class library.
Dependencies: none.
In my test solution, it contains only one class, a model called Person.
Person.cs
namespace ClassLibrary1
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}
Name: EFClassLibrary.
Type: .NET Standard 2.0 class library.
Dependencies:
This project, in my test solution, contains only one class: the database context.
ClientDbContext.cs
using ClassLibrary1;
using Microsoft.EntityFrameworkCore;
namespace EFClassLibrary
{
public class ClientDbContext : DbContext
{
const string connectionString = "Server=(localdb)\\mssqllocaldb;Database=ClientDb;Trusted_Connection=True;";
public ClientDbContext() : base() { }
public ClientDbContext(DbContextOptions<ClientDbContext> options) : base(options) { }
public DbSet<Person> People { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString);
}
}
}
In this class a defined an used the connection string to connect to the database (assuming it's LocalDb SQL Server). If you want to put the connection string in a config file, you could add a shared config file in your solution and then reference that file in your App.config
file (for more informartion take a look at this page)
In order to be able to add migrations on this project without setting as startup project other projects, you must set the target framework. Right click on the project and click on the Edit EFClassLibrary.csproj entry. Below the <TargetFramework>netstandard2.0</TargetFramework>
line, you should add another line which specify which framework you want to target. To target the .NET Framework 4.7 you should add
<TargetFramework>net47</TargetFramework>
A list of all allowed values can be found here.
My EFClassLibrary.csproj look like the code below after adding the .NET Framework 4.7 as target.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net47</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />
</ItemGroup>
</Project>
Now you are ready to add your first migration. Open the Package Manager Console and set as default project the EFClassLibrary. Also, set that project as startup project (right-click on the project and click on the Set as startup project entry).
Type
PM> Add-Migration Initial
then
PM> Update-Database
Name: WpfApp1.
Type: WPF Application using .NET Framework 4.7.
Dependencies:
In this project I added no files. A just edited the MainWindow.xaml.cs
file to check that everything works correctly.
MainWindow.xaml.cs
using ClassLibrary1;
using EFClassLibrary;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var db = new ClientDbContext();
db.People.Add(new Person()
{
Name = "Omar"
});
db.SaveChanges();
}
}
}
Hope it helps :)
Upvotes: 29
Reputation: 10919
What microsoft suggests here is to create a new class library for the migrations, then move your model snapshots files and migrations to the new class library.
If you haven't added any, add one to the DbContext project then move it.
Then configure migration assembly:
options.UseSqlServer(
connectionString,
x => x.MigrationsAssembly("MyApp.Migrations"));
And then add reference to your migrations assembly from your startup assembly:
note:
If this causes a circular dependency, update the output path of the class library:
<PropertyGroup>
<OutputPath>..\MyStarupProject\bin\$(Configuration)\</OutputPath>
</PropertyGroup>
for a second reference use Ben Cull's guide on EntityFramework Core migration for class library projects.
Upvotes: 7
Reputation: 497
In your startup project you have to create a factory which will implement IDesignTimeDbContextFactory and create the DBContext for you.
public class DBContextFactory : IDesignTimeDbContextFactory<DBContext>
{
public DBContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<DBContext>();
// Row numbers for paging adds support for old SQL server 2005+. See more:
// https://learn.microsoft.com/en-us/ef/core/api/microsoft.entityframeworkcore.infrastructure.sqlserverdbcontextoptionsbuilder
optionsBuilder.UseSqlServer("Server=localhost;Database=DatabaseName;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=SSPI", x => x.UseRowNumberForPaging());
return new DBContext(optionsBuilder.Options);
}
}
Upvotes: 6