DSP2017, DSP2017 Project

EF Migrations in .Net Core can be annoying!

The time has came for generating EF Migrations. It’s nothing special, so I didn’t plan to spend much time on it. But as it sometimes happen, I was encountering problem after problem so I decided it’s worth writing them down.
But let’s start from the beginning!

The first problem was really strange – I selected Remaster.EntityFramework project as the default one and then typed ‘Add-Migration‘ in Package Manager Console. The error I received was rather unusual, considering the fact that the project has Microsoft.EntityFrameworkCore.Design installed.

System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.EntityFrameworkCore.Design, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
File name: 'Microsoft.EntityFrameworkCore.Design, Culture=neutral, PublicKeyToken=null'

I examined my Nuget packages and realized that ‘Microsoft.EntityFrameworkCore.Tools‘  version is rather low so I upgraded all of the EF packages to the latest version. Again, I types ‘Add-Migration‘ and… The same error! It made me a little bit confused – I have the package installed so what’s the problem?

The less probable solutions always works!

I was running out of other brilliant ideas so I just did what I always do when I have problems with migrations – I set Remaster.EntityFramework project as startup project. With really little hope I typed Add-Migration aaand… Received very nice error.

No parameterless constructor was found on 'ReMasterDbContext'. Either add a parameterless constructor to 'ReMasterDbContext' or add an implementation of 'IDbContextFactory<ReMasterDbContext>' in the same assembly as 'ReMasterDbContext'.

At least I knew what was wrong! Yay, let’s fix it. I created the class that inherits from IDbContextFactory:

public class MigrationsContextFactory : IDbContextFactory<ReMasterDbContext>
{
    public ReMasterDbContext Create(DbContextFactoryOptions options)
    {
        return new ReMasterDbContext("DefaultConnection");
    }
}

The next attempt to add migration and the next error, this time claiming to occur in OnConfiguring() method:

System.ArgumentNullException: Value cannot be null.
Parameter name: connectionString
at Microsoft.EntityFrameworkCore.Utilities.Check.NotEmpty(String value, String parameterName)
at Microsoft.EntityFrameworkCore.SqlServerDbContextOptionsExtensions.UseSqlServer(DbContextOptionsBuilder optionsBuilder, String connectionString, Action`1 sqlServerOptionsAction)

….

But hey! What’s wrong with that method? It looks like this:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
   var configuration = ConfigurationHelper.Get(Directory.GetCurrentDirectory());
   var connString = configuration.GetConnectionString("DefaultConnection");
   optionsBuilder.UseSqlServer(connString);
}

Yeah, there is my custom configuration-reading class but I tested it in other places of my project and it worked just fine so it can’t be a problem… Or can it be? When I think about it a little I came to the conclusion that there might be a problem with the path to the appsettings.json. I managed to confirm my conclusion by changing  Directory.GetCurrentDirectory()  with the strict path to Remaster project. Well, life is full of surprises! 😉

OK, let’s change the path and stop playing with this silly subject! There is a lot of challenging code to write! 😉 But first – I have to find out…

How to get path to the web project from class library

Piece of cake! I added a public class RemasterContext with the following method:

public static string GetPathToProjectFolder()
{
    var mainDirectoryPath = Path.GetDirectoryName(typeof(RemasterContext).GetTypeInfo().Assembly.Location);
    var mainDirectoryInfo = new DirectoryInfo(mainDirectoryPath);
    var pathToSln = Path.Combine(mainDirectoryPath, "ReMaster.sln");

    while (!System.IO.File.Exists(pathToSln))
    {
        if (mainDirectoryInfo.Parent == null)
        {
            break;
        }

        mainDirectoryInfo = mainDirectoryInfo.Parent;
        pathToSln = Path.Combine(mainDirectoryInfo.FullName, "ReMaster.sln");
    }
    var path = Path.Combine(mainDirectoryInfo.FullName, @"src\ReMaster");
    return path;
}

I put this class in Remaster.Core project but it really doesn’t matter – I could have added it in Remaster.EntityFramework. The important fact is that the class can get path to the main directory of web project, where the configuration file is stored.
Using this method to get the path to the appsettings.json helped me to get rid of the error about null connection string. I thought the problems were over but…

It’s time for Database-Update!

And the next error! This time ‘System.Data.SqlClient‘ was in the wrong version.

System.IO.FileNotFoundException: Could not load file or assembly 'System.Data.SqlClient, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'System.Data.SqlClient, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerConnection.CreateDbConnection()
at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_1.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Could not load file or assembly 'System.Data.SqlClient, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.

I really started losing my patience! What the f*ck is wrong with you, .Net Core? Do I really expect too much from you?
I downgraded System.Data.SqlClient to the 4.1.0 version. Nope, the same error.
I downgraded all the EF packages. Nope.
I upgraded all of them to the latest version. Try to guess the result ;)! Yes, nothing happened.
Well, I got quite frustrated. Google to the rescue!


In fact, the problem wasn’t very popular… After reading some advices that didn’t help me at all, I decided to just copy most of the project.json from this thread.
Aaaand in some magical way, it works. But the comment of the thread’s author describes all the beauty of programming .Net Core apps:

[it] solved my problem, I’m not sure why so if somebody can explain why this project.json fixed the problem I would thank him/her all my life 😀

Featured image by Viktor.

Share this: