In Entity Framework by using Enable-Migrations a Migrations folder is created containing a Configuration inherited from DbMigrationsConfiguration like this:
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
...
}
All the created migrations which are created using Add-Migration are placed in the Migrations folder too.
public partial class Init: DbMigration
{
public override void Up()
{
...
}
public override void Down()
{
...
}
}
I didn't find any code that relates these two together ( for example having a configuration property in migrations). The only relation I found is that both are placed in same folder. If I have more than 1 DbContext and consequently more than 1 Configuration, I'm wondering how these DbMigrations are distinguished?
Question: How DbMigration classes are related to a Configuration?
They are related by convention. By default, it will store the migrations in a root folder called Migrations. You can override this in the constructor of the config (https://msdn.microsoft.com/en-us/library/system.data.entity.migrations.dbmigrationsconfiguration(v=vs.113).aspx) or when you enable-migrations:
public Configuration()
{
AutomaticMigrationsEnabled = true;
MigrationsDirectory = #"Migrations\Context1";
}
For multiple contexts, create a different config and folder for each by using -ContextTypeName ProjectName.Models.Context2 -MigrationsDirectory:Migrations\Context2. Here is a walkthrough: http://www.dotnettricks.com/learn/entityframework/entity-framework-6-code-first-migrations-with-multiple-data-contexts
When you run the update-database command, the database operations in the up() method in the latest DbMigration derived classes is performed. If that is successful, the commands in the Configuration class are executed. One of those methods is the seed() method where you can optionally add code to plug values into your tables after a migration. When you specify a target migration (presumably earlier than the latest), the migration works through the chain of down() methods in the migration classes to get to the version you wanted.
Related
I am trying to use EF Core 3.0 migrations with a hybrid of an existing tables and new tables built with code first. To prevent the scaffolding of the existing tables I would like to decorate the model class with an attribute (fluently or annotations) so that the migration code generation for those tables is skipped but the model is still built into the DbContext class.
The approach I'm taking is to add the following lines to OnConfiguring
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, SkipMigrator>();
And then creating a SkipMigrator with the following code
public class SkipMigrator:SqlServerMigrationsSqlGenerator
{
public SkipMigrator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations){}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
if (operation.FindAnnotation("SkipMigrations")!=null)
{
Console.WriteLine("Skipping table:");
}
else
{
base.Generate(operation,model,builder);
}
}
}
I assumed that the Generate method was what triggered the creation of the migration code file but it never gets called. Id there a different place I should be intercepting the code generation?
If there a different/simpler way to tell migrations to skip tables yet still keep them in my DbContext?
Your means of trying to create your own IMigrationsSqlGenerator was correct, and I've used that approach before to alter the SQL that is generated as part of a migration.
services.AddDbContext<MyDbContext>(opt =>
{
opt.UseSqlServer();
opt.ReplaceService<IMigrationsSqlGenerator, SkipMigrator>();
});
However, as of EF Core 5.0 it's now much easier to exclude specific tables from migrations using the ExcludeFromMigrations() method on a TableBuilder:
public class ReportingContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable(nameof(Users), t => t.ExcludeFromMigrations());
}
}
More info: https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-efcore-5-0-rc1/#exclude-tables-from-migrations
If there a different/simpler way to tell migrations to skip tables yet still keep them in my DbContext?
Yes, but it requires a different approach.
Instead of using your current DbContext class for creating migrations, create a SecondDbContext class only for the very purpose of creating migrations. This new SecondDbContext class will hold the DbSets<T> that you want EF to do its migrations on.
Then simply specify the second context when calling add-migration UpdateTable -c SecondDbContext and then update-database -c SecondDbContext.
I'm using doing database first development and using Scaffold-DbContext to create my entity models project. In that project, I have a partial dbContext where I override methods like SaveChangesAsync to set certain properties like, "LastModifiedBy".
I'm looking to soft-delete records using dateDeleted/userDeleted columns. When I go to override the OnModelCreating routine, I see that it's already defined on the auto-generated partial context.
I'm trying to do something like the following:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SomeEntity>().HasQueryFilter(x => x.DateDeleted == null);
}
Does anyone have any suggestion as to how I can do this in a partial class way that still allows me to regenerate the original context on the fly using Scaffold-DbContext?
Also note, I'm using .NET Core 2.1.5
ANSWER FROM David Browne - Microsoft
Add a static property on the partial context
public static bool GlobalFiltersAdded { get; set; } = false;
Then add a routine to add your filters:
private void AddGlobalFilters(ModelBuilder modelBuilder){
SomeContext.GlobalFiltersAdded = true;
}
Then in your generated context add the following:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
AddGlobalFilters(modelBuilder);
//OTHER LOGIC WILL BE HERE
}
Options:
1) Delete the generated OnModelCreating after re-scaffolding. It's already a manual process, and generates a compile error if you forget.
2) Use a DbContext inherited from the generated DbContext.
3) Use a 3rd party tool or library like: EF Core Power Tools
I have separated my solution in separate projects, a DAL project with entity framework and an ASP.NET MVC project.
I want to use DropCreateDatabaseIfModelChanges, but I don't know where to put it to make it work.
I've tried to put it in the web.config of the MVC project and the app.config of the DAL project (both by making use of the context element), I've tried putting it in the global.asax (Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BreakAwayContext>());), I've tried a custom initialization class, but none of these seem to work.
If possible, I don't want to make use of migrations. How can I make it work?
You could create a class to implement CreateDatabaseIfNotExists and call Database.SetInitializer function in Application_Start().
-DbInitializer
public class MyDbInitializer : CreateDatabaseIfNotExists<MyDbContext>
{
protected override void Seed(MyDbContext context)
{
//Data initializing...
}
}
-Application_Start
protected void Application_Start()
{
Database.SetInitializer(new MyDbInitializer());
}
The database will be create when running the application.
And if you would like to do a automatic migration of database, use MigrateDatabaseToLatestVersion class
public class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
this.AutomaticMigrationsEnabled = true;
}
}
-Application_Start
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext,Configuration>());
Howerver, I recomand that using migraion commands will be more flexible. See this walkthru: Overview of Entity Framework Code First Migrations with example, by Bhavik Patel.
I guess by 'database initialization' you actually mean 'updating the database schema'.
Set the EfRepository as start up project of the solution
Open the Package manager console Choose EfRepository as default project
Run the following commands:
Enable-Migrations -ConnectionStringName "EfDataRepository"
Add-Migration Initial -ConnectionStringName "EfDataRepository"
Update-Database -ConnectionStringName "EfDataRepository" -Script -SourceMigration:0
This will give you a .sql script. Execute it against your database (and usually store it as part of the solution - either Create.sql or some kind of a migration .sql, depends on whether you already have a schema or you are creating it from scratch).
Of course, replace EfDataRepository with the data connection name from your .config file.
I am new to Code first, can you tell me how i can have all Up and Down methods for all tables in the database like below(given for one table)
public partial class abc: DbMigration
{
public override void Up()
{
AddColumn("dbo.UserTasks", "ServiceTechReason", c => c.Long());
}
public override void Down()
{
DropColumn("dbo.UserTasks", "ServiceTechReason");
}
}
i want all three types for a table viz .cs , .Designer.cs , .resx.
2) Can you explain the above example, i pick it from somewhere on internet i was searching for this but found nothing. is abc is my table name in database?
Provide me link if it is already answered.
EDIT
As mentioned by #scheien i already tried those commands they do not automatically override up and down methods for a table
Creating migrations is done by running the command Add-Migration AddedServiceTechReason.
This assumes that you have already enabled migrations using the Enable-Migrations command.
To apply the current migration to the database, you'd run the Update-Database. This command will apply all pending migrations.
The point of Code-First migrations is that you make the changes you want to your Entity(ies), and then add a new migration using the Add-Migration command. It will then create a class that inherits DbMigration, with the Up() and Down() methods filled with the changes you have made to your entity/entities.
As per #SteveGreenes comment: It does pick up all changes to your entities, so you don't need to run it once per table/entity.
If you want to customize the generated migration files, look under the section "Customizing Migrations" in the article listed.
All these commands are run in the package manager console.
View -> Other windows -> Package Manager Console.
Here's a great article from blogs.msdn.com that explains it in detail.
I have only 2 days experience with EF Migrations, so please be kind...
I have a fairly large existing WPF WCF MVVM EF 4.1 solution which needs to be migrated to EF 4.3.1 and begin using Migrations. The solution's "Services" project contains four DbContext's, each in its own namespace, and each associated with its own database.
Before I began modifying the large solution, I did some experimentation with a small sample console app with only a single project and two DbContexts, mostly based on the example provided by "e10"
(EF 4.3 Auto-Migrations with multiple DbContexts in one database). The sample app works well, and I can do add-migration and update-database separately for the two contexts (by specifying the -configuration parameter).
But when I tried to replicate the same approach with the "real" (large) solution - with four DbContexts - I ran into a problem: when I invoke add-migration in PMC and specify any of the four configuration names, add-migrations gets an exception saying it can't load the Services assembly.
Here's what I did with the large solution:
1) Added the EF 4.3.1 NuGet package to my Core, Services and UI projects (this last bit may be important).
2) created a Migrations folder in my Services project and manually created a Configuration.cs file containing four classes which inherit from DbMigrationsConfiguration<type>, where type is App, Catalog, PortfolioManagement or Scheduler. (code is below)
3) added a property to one of the model classes associated with the App DbContext, so there would be something to migrate
4) from the PMC, invoked add-migration:
PM> add-migration App_AddNewProperty -config App
Note that I didn't do "Enable-Migrations" because, as e10 said in his post:
" You dont need to enable migration since you already did with the ... classes above" (referring to the classes in Configurations.cs).
5) add-migration gets exception: Could not load file or assembly 'MyApp.Services' or one of its dependencies
I enabled binding-failure logging, and the failure log shows that it's trying to locate the Services assembly in the UI's bin/debug folder, rather than in the Services project).
And it fails the same way even if I have the Default Project in the PMC set to the Services project (Default Project defaults to the UI project).
I suspect this is caused by the UI not having a reference to the Services assembly (it has a WCF Service Reference, but not an assembly reference). But if this is the problem, how do I force PMC to not start at the UI project? Or can I "unassociate the UI project from the EF package"?
Thanks!
DadCat
Configurations.cs:
namespace MyApp.Services.Migrations
{
internal sealed class App : DbMigrationsConfiguration<Geophysical.Skimmer.Services.App.Repository.ModelContainer>
{
public App()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.Services.App.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.App.Repository.ModelContainer context)
{
... no code here
}
}
internal sealed class Catalog : DbMigrationsConfiguration<Geophysical.Skimmer.Services.Catalog.Repository.ModelContainer>
{
public Catalog()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.Catalog.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.Catalog.Repository.ModelContainer context)
{
... no code here
}
}
internal sealed class PortfolioManagement : DbMigrationsConfiguration<Geophysical.Skimmer.Services.PortfolioManagement.Repository.ModelContainer>
{
public PortfolioManagement()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.PortfolioManagement.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.PortfolioManagement.Repository.ModelContainer context)
{
... no code here
}
}
internal sealed class Scheduler : DbMigrationsConfiguration<Geophysical.Skimmer.Services.Scheduler.Repository.ModelContainer>
{
public Scheduler()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.Services.Scheduler.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.Scheduler.Repository.ModelContainer context)
{
... no code here
}
}
}