6

I would like to execute some LINQ in Up method of migrations. Problem is I don't know how can I get DbContext instance?

This is code generated by migrations add:

public partial class MyTableAddFieldTitle : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<string>( name: "Title", table: "MyTable", nullable: true); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( name: "Title", table: "MyTable"); } } 

I would like to add something like that in Up method:

protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<string>( name: "Title", table: "MyTable", nullable: true); var context = ?????; //Actual code is much more complicated, but the principle is the same. foreach (var item in context.Set<DbMyTable>()) item.Title = item.SomeStringColumn; context.SaveChanges(); } 

Problem is how to get context instance? I tried with DI in constructor:

protected MyTableAddFieldTitle(MyContext context) { } 

but I get error:

MissingMethodException: No parameterless constructor defined for this object. System.RuntimeTypeHandle.CreateInstance(RuntimeType type, bool publicOnly, ref bool canBeCached, ref RuntimeMethodHandleInternal ctor)

4
  • 2
    Are you mixing seeding and migration? Usually, you wouldn't want to deal with your DbContext in the migration stage as it's just being built. If you need to seed data, do it properly: learn.microsoft.com/en-us/ef/core/modeling/data-seeding Commented Mar 21, 2018 at 7:52
  • Maybe, but I think not. I just created new field on a table. I need to populate this field. I understand seeding as a way to fill database with some records. I will take a look. Thanks for now. Commented Mar 21, 2018 at 7:54
  • @haim770 You were right. In Up method field Title doesn't yet exists on database. Commented Mar 21, 2018 at 8:07
  • 1
    Of course it doesn't exist. Migrations just create the necessary abstract operations needed. Think of them as migration command builders. Thus you are limited to the available commands, with Sql being the only method allowing you to specify arbitrary SQL command(s). No LINQ, sorry. Commented Mar 21, 2018 at 9:38

2 Answers 2

4

I found solution.

In Startup class I defined static variable:

public static Func<MyContext> ContextFactory; 

In constructor of Startup class I assigned variable:

public Startup(IHostingEnvironment env, IConfiguration config) { MyContext GetContext(IConfiguration configuration, IHostingEnvironment environment) { var builder = new DbContextOptionsBuilder<MyContext>(); builder.UseSqlServer(configuration["ConnectionStrings:Web"], b => b.MigrationsAssembly("Web.Hosting")); if (environment.IsDevelopment()) builder.EnableSensitiveDataLogging(); return new MyContext(builder.Options); } ContextFactory = () => GetContext(config, env); } 

Then in Migrations I simply call ContextFactory:

var context = Startup.ContextFactory(); context.Set<DbMyTable>().Where(.... 

To avoid error field does not exists I create 2 migration files (dotnet ef migrations add).
First adds field:

protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<string>( name: "Title", table: "MyTable", nullable: true); } 

And second (empty) executes query:

protected override void Up(MigrationBuilder migrationBuilder) { var context = Startup.ContextFactory(); context.Set<DbMyTable>().Where(.... } 
Sign up to request clarification or add additional context in comments.

Comments

0

Can now use MigrationBuilder.SQL() with a string and it just works as expected. See .Net Learn Custom Migrations Operations

protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<string>( name: "Title", table: "MyTable", nullable: true); migrationBuilder.Sql( "update MyTable set Title = SomeStringColumn" ); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.