Jensen
Jensen

Reputation: 1329

EF 6.4 Migration with .NET Core and ef6.dll yields in NullReferenceException

I am creating a migration bundle for external usage by our database team to execute Entity Framework Migrations. The bundle is composed of an Entity Framework Project (which includes all our migrations) and the ef6.dll migration tool from the Entity Framework 6.4 NuGet package, which is located at ef64package/tools/netcoreapp3.0/any.

The migration bundle also contains a simple PowerShell script that executes ef6.dll somehow like this:

dotnet .\ef6.dll database update --verbose --assembly MyApp.Core.EF.dll --connection-string "Data Source=localhost;Initial Catalog=MyApp;Trusted_Connection=True;" --connection-provider "System.Data.SqlClient"

Using the migrate.exe (in previous versions of Entity Framework) and ef6.exe for .NET Framework this bundle worked just fine. But after switching to .NET Core and ef6.dll the execution results in the following error:

Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Data.Entity.Internal.LazyInternalConnection.CreateConnectionFromProviderName(String providerInvariantName)
   at System.Data.Entity.Internal.LazyInternalConnection.InitializeFromConnectionStringSetting(ConnectionStringSettings appConfigConnection)
   at System.Data.Entity.Internal.LazyInternalConnection.Initialize()
   at System.Data.Entity.Internal.LazyInternalConnection.get_Connection()
   at System.Data.Entity.Internal.LazyInternalContext.get_Connection()
   at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbProviderInfo modelProviderInfo, AppConfig config, DbConnectionInfo connectionInfo, Func`1 resolver)
   at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbConnectionInfo connectionInfo)
   at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext, DatabaseExistenceState existenceState, Boolean calledByCreateDatabase)
   at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
   at System.Data.Entity.Infrastructure.Design.Executor.CreateMigrator(DbMigrationsConfiguration configuration)
   at System.Data.Entity.Infrastructure.Design.Executor.UpdateInternal(String targetMigration, Boolean force, DbConnectionInfo connectionInfo, String migrationsConfigurationName)
   at System.Data.Entity.Infrastructure.Design.Executor.Update.<>c__DisplayClass0_0.<.ctor>b__0()
   at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.Execute(Action action)
Object reference not set to an instance of an object.

A very generic error, but according to the EF source code (https://github.com/dotnet/ef6/blob/02104044fae2aca5d3a1f50f55f4af940a3c64a8/src/EntityFramework/Internal/LazyInternalConnection.cs), i looks like a DbProviderFactory registration is missing or the providerInvariantName is messed up in the process. I've got a feeling this is a side effect due to a missing configuration or dependency. But so far i could not figure out how to fix this error.

Any ideas on how to fix this error? Also other approaches in creating a standalone migration bundle are highly welcome.

Thanks in advance!

Upvotes: 4

Views: 1727

Answers (2)

Thomas
Thomas

Reputation: 31

Migrations for Entity Framework (6.4.4) + Net Core 3.x require an executable application as an entry point. (The 'old' migrations were fine with just the dll containing database context.)
You just need to add an executable application (or create a dummy commandline app), and specify it in the command. This is non-obvious, because all examples assume you have only single project with single dll, and do not warn about this.

If you check the syntax of ef6.dll, you will see that parameter for main application dll... is not present.
But, it can be worked around by using the undocumented dotnet exec syntax.

dotnet exec --depsfile Your.App.Project.deps.json --runtimeconfig Your.App.Project.runtimeconfig.json .\ef6.dll" database update --verbose --assembly Your.Database.Project.dll --connection-string "<standard sql string>"

Please note that .deps file and .runtimeconfig come from your executable application, and that all .dll files for said application must be present in the folder (tool will check that). The remaining parameters are standard for ef6.dll - name of .dll file containing database context, connection string, optionally provider type.

Some caveats:

  • Ensure the '/runtimes' folder (if present) is copied along with application dll files. Missing that will give an obscure error about missing .dll that is already present in an folder, but apparently Microsoft.Data.SqlClient.dll needs some secret child dll in runtimes folder.
  • .net Core no longer has packages folder, so you have to figure out where they are on given machine, or copy the ef6.dll into your source.
  • When testing locally, make sure you un-escape the double slashes in your connection string. Its no fun to get another obscure error when you used Server=.\\SQLEXPRESS instead of Server=.\SQLEXPRESS

Author of this post officially hates the ef6.dll now.

Upvotes: 3

Alex
Alex

Reputation: 18556

This is how I got it to work:

dotnet exec --depsfile MyApp.Core.EF.json --runtimeconfig MyApp.Core.EF.runtimeconfig.json "....\packages\entityframework\6.4.0\tools\netcoreapp3.0\any\ef6.dll" database update --assembly MyApp.Core.EF.dll --connection-string "xxx" --verbose

See also this Github issue or Entity Framework Migration Azure DevOps Release Pipeline.

Upvotes: 2

Related Questions