ASP.NET Core Entity
Framework Core .NET Core CLI CLI command
▌Introduction
So in .NET Core
projects, we can use EF Command to execute database migrations.
However, in
current DOTNET Core 1.0.0-preview, the EF Command only works on the runnable
projects (such like Website, Console…)*.
See issue :
|
And that means
we could only put our DbContext and DAOs in the website project?
That seems it will
mess up the website with too much responsibility within it.
In this
article, we will try to make a clean architecture for Entity framework Core and
let the EF Command works.
▋Related articles
▋Supported framework
EF supports .NET Core CLI commands on these frameworks:
▋.NET Framework 4.5.1 and newer. (“net451”, “net452”, “net46”, etc.)
▋.NET Core App 1.0. (“netcoreapp1.0”)
▌Environment
▋Visual Studio 2015
Update 3
▋DOTNET Core 1.0.0- DOTNET Core 1.0.0-preview
▌Implement
Create two projects
1.
Sample.Website : ASP.NET
Core
2.
Sample.DAL : Class library
(.NET Core)
Sample.DAL will have the DbContext, Database Models.
▋Data Access layer (DAL)
Install the following package(s):
1.
Microsoft.EntityFrameworkCore.SqlServer
(1.0.1)
▋/Models/DAO/Customer.cs
You can create your own DAO. This one is just for
reference.
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(100)]
[Required]
public string Name { get; set; }
[StringLength(100)]
public string Phone { get; set; }
public int Age { get; set; }
[StringLength(200)]
public string Description { get; set; }
}
|
▋/DbContext/NgDbContext.cs
public class NgDbContext : DbContext
{
public NgDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
|
▋Presentation Layer (Website)
Install the packages.
1.
Microsoft.AspNetCore.Identity.EntityFrameworkCore:
"1.0.0-rc2-final"
2.
Microsoft.EntityFrameworkCore:
"1.0.1"
3.
Microsoft.EntityFrameworkCore.Tools:
"1.0.0-preview2-final"
(For Sql server)
4.
Microsoft.EntityFrameworkCore.Relational:
"1.0.1"
5.
Microsoft.EntityFrameworkCore.SqlServer:
"1.0.1"
The most important is Microsoft.EntityFrameworkCore.Tools,
which supports the EF Command Line tooling.
▋Test if the EF
command works
Open command line or Powershell or Package
Management Console*, change
directory to the root of the website, and type the command to see if EF Command
works.
$> dotnet ef --version
|
Or $> dotnet ef --help
to see the helps.
Sometimes you may see the following error message:
dotnet ef
throws Could not load file or assembly Microsoft.DotNet.Cli.Utils
Then go clear(delete) the nuget caches
1.
C:\Users\{LoginName}\.nuget\packages\.tools\Microsoft.EntityFrameworkCore.Tools
2.
C:\Users\{LoginName}\.nuget\packages\Microsoft.EntityFrameworkCore.Tools
and restore them under the website’s root.
$> dotnet restore
|
Also see this reference.
▋Start Migration
The original add-migration and update-database commands
are simple, like this
$> dotnet ef migrations add [migration_name]
–c [DbContext_name]
$> dotnet ef database update [migration_name]
PS.
[migration_name] = “0” will revert all migrations, or skip this argument to
apply all pending migrations.
However, while we put the DbContext in the other project but
not the one we are running EF Command, so we have to appoint the target project
with DbContext and the startup project for EF Command.
▋Add migration
dotnet ef --project ../Sample.DAL
--startup-project . migrations add [migration_name] -c [DbContext_name]
|
▋Update database
dotnet ef --project ../Sample.DAL
--startup-project . database update
|
▋Implement IDbContextFactory<DbContext>
Okay, now it seems works but right away we get the
following error:
No parameterless
constructor was found on 'NgDbContext'. Either add a parameterless constructor
to 'NgDbContext' or add an implementation of 'IDbContextFactory<NgDbContext>'
in the same assembly as 'NgDbContext'.
This error is telling us that the migration would need to
create a DbContext instance to get things done.
We will create a class and implement IDbContextFactory<TContext>
in Sample.DAL.
▋MigrationFactory.cs
public class MigrationFactory : IDbContextFactory<NgDbContext>
{
public NgDbContext Create()
{
var builder = new DbContextOptionsBuilder<NgDbContext>();
builder.UseSqlServer(Configuration.DEFAULT_CONNECT_STR);
return new NgDbContext(builder.Options);
}
}
|
PS. I create another class: Configuration, to store the database connection
string.
Re-run the add-migration and update-database commands,
the CLI create/update the table: Customers for us.
For more EF Command usage, take a look at docs.efproject.net
▋How to initialize data
The data initializing function is on the
roadmap of Entity framework Core team and will be released in the future.
So far we can make a trick to achieve
the data-initializing.
Create a class: Configuration,
▋Configuration.cs
public class Configuration
{
//Connection string for code first
public static string DEFAULT_CONNECT_STR =
"Server=.;Database=XXX;Trusted_Connection=True;MultipleActiveResultSets=true";
public void Seed()
{
var dbContext = DbContextFactory.Create(DEFAULT_CONNECT_STR);
#if (Seed)
this.initCustomers(dbContext);
#endif
}
private void initCustomers(NgDbContext dbContext)
{
dbContext.Customers.Add(new Customer { Name = "JB
Lin",
Phone = "0933XXXXXX", Age = 35, Description = "JB is a good programmer :)" });
dbContext.Customers.Add(new Customer { Name = "Leia
Lin",Phone = "-", Age = 3, Description = "A cute girl!" });
dbContext.SaveChanges();
}
}
|
We should call
the Seed function after running add-migration command but before
update-database command. Add the Seed function to the migration class which is created by the
CLI.
So when the
update-database command is executed, the data is also be initialized.
▌Reference