Moving from EF6 to EF Core
I always liked using the latest and greatest and in most of the cases this choice comes as a great challenge. We started building a project a couple of years ago in ASP.NET vNext (or whatever they called it at the time) beta4 with all the latest tooling: dnvm, dnx, dnu, etc... We were not brave enough at the time to use Entity Framework 7 for obvious reasons, it was simply not ready, so we used EF6 and it was kind of happy days until we were ready to release the first version of the application and ready to run enable migrations
then we hit a massive roadblock, EF6 tooling wasn't available for that version of .NET. Something that was supposed to take 5 minutes ended up taking the whole week, that wasn't happy days at all I gotta say. At the end, we found a solution on Gabriel Garcia's GitHub repository. He saved the day.
That was a bit of the background and paths we took. Now, as I'm writing this post, I'm facing a new challenge: Moving from EF6 to EF Core. I just started the migration and what I'm gonna do here is write down every change I make on the fly to get to the other side. When or If I get to the other end, I'll tidy up this post and publish it.
If you are not sure about moving, check this post to ensure that the changes won't cause you too much grief.
As per the Entity Framework Blog, they do NOT advise moving from EF6 to EF Core:
Because of the fundamental changes in EF Core we do not recommend attempting to move an EF6.x application to EF Core unless you have a compelling reason to make the change. You should view the move from EF6.x to EF Core as a port rather than an upgrade.
I'll do it all the same. Using EF6 in a .NET Core application is already a compelling enough reason to move. So here we go, these are the changes I had to make.
Replace Dependencies
Uninstall EF6 packages
EntityFramework
Install EF Core packages
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.Design
Code Updates
- Namespace
System.Data.Entity
replaced byMicrosoft.EntityFrameworkCore
HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)
replaced byValueGeneratedNever();
- The base constructor of
DbContext
doesn't have a single string parameter for the connection string. We now have to inject theDbContextOptions
OnModelCreating(DbModelBuilder modelBuilder)
becomesOnModelCreating(ModelBuilder modelBuilder)
. Simple change, but change all the samemodelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
is no longer available which means thatEntityTypeConfiguration
is also not available, so I had to move all my entity configuration toOnModelCreating
((IObjectContextAdapter)context).ObjectContext.ObjectMaterialized
is no longer available. I was using that to extend the DbContext to convert all dates in an out to Utc. I haven't found a replacement for that yet.ComplexType
is no longer available. I had to change the model structure a bit to accomodate this.MigrateDatabaseToLatestVersion
is no longer available so I had to add the below to mystartup.cs
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetService<SbDbContext>().Database.Migrate();
}
WillCascadeOnDelete(false)
becomesOnDelete(DeleteBehavior.Restrict)
HasOptional
is no longer relevant as per postIDbSet
becomesDbSet
DbSet<T>.Add()
no longer returnsT
butEntityEntry<T>
var entry = context.LearningAreaCategories.Add(new LearningAreaCategory());
//that's if you need to use the entity afterwards
var entity = entry.Entity;
IQueryable<T>.Include(Func<>)
now returnsIIncludableQueryable<T,Y>
instead ofIQueryable<T>
, same applies forOrderBy
. What I did was moving all the includes and orderbys to the end.
var query = context.Students;
if(filter.StudentId > 0)
{
query = query.Where(s => s.Id == filter.StudentId);
}
return query.Include(s => s.School).OrderBy(s => s.Name);
Migrations
We had plenty of existing EF6 migrations and, as per post, it's pretty much unfeasible to port EF6 migrations to EF Core. So I did what they suggested and deleted the entire Migrations
folder and started fresh with a new EF Core migration with the command add-migration init-with-core
. enable migrations
is no longer required for EF Core as add-migration
does its job.
1st attempt to run add-migration
Could not load file or assembly 'Microsoft.EntityFrameworkCore.Design' or one of its dependencies. The system cannot find the file specified.
2nd, 3rd, 4th attempts had the same issue then I restarted Visual Studio`
5th attempt I started getting
Unable to determine the relationship represented by navigation property 'Response.Students' of type 'ICollection<Student>'. Either manually configure the relationship, or ignore this property from the model.
Where it seems that EF Core doesn't automatically create the relationships, so I had to add the relationships one by one. I haven't decided yet if that's a good or a bad thing.
Finally, I got
PM> add-migration init-with-core
To undo this action, use Remove-Migration.
Other things worth mentioning
- Foreign keys in EF Core generate shadow properties using the pattern
[Entity]Id
where in EF6 it uses[Entity]_Id
. To avoid future conflicts, before moving to EF Core, add foreign keys as a normal property to the entity. More info about shadow properties here
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
//foreign key added as a normal property
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
- If you're uncertain that EF Core and EF6 will generate the same database schema, use tools like SQL Compare to double check.
Hope it helps.
Cheers.