Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConfigurationManager return null when using to extract database connection string when add migration

I am using Entity Framework Core in my .NET Core console app. I store my connection string in the App.config file and use ConfigurationManager in context class to access the connection string.

When I want to add new migration to my project I get the following error:

System.NullReferenceException: Object reference not set to an instance of an object.

at EF_tut.Context.OnConfiguring(DbContextOptionsBuilder optionsBuilder) in C:\..\Context.cs:line 12
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.Internal.InternalAccessorExtensions.GetService[TService](IInfrastructure1 accessor)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func
1 factory) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType) at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.b__0() at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

Object reference not set to an instance of an object.

This is my App.config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
         <add name="EfTutDb" 
              connectionString="Data Source=.;Initial Catalog=EF_tut;Integrated Security=True;"/>
    </connectionStrings>
</configuration>

and this is my context class:

class Context : DbContext
{
    public DbSet<Student> Students { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(ConfigurationManager.ConnectionStrings["EfTutDb"].ConnectionString); // Line 12.
    }
}

When I try get connection string using ConfigurationManger in the main method, I get the connection string but when I add migration, I get Null Reference error.

like image 869
eyvaz Avatar asked Apr 12 '19 20:04

eyvaz


2 Answers

Maybe it's a bit workaround.. but this will work:

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var configuration = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddXmlFile("App.config")
            .Build(); 

        optionsBuilder.UseSqlServer(configuration.GetValue<string>("connectionStrings:add:EfTutDb:connectionString"));
    }
}
like image 121
imported_lis Avatar answered Oct 09 '22 19:10

imported_lis


The EF Core instantiates the DB context at design-times such as Add-migration or Update-Database, to gather details about the entities and map them.

https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#design-time-dbcontext-configuration

But the App.config of your project is not accessible using the ConfigurationManager, since the EF core design-time is another application with its own application settings.

I faced the same problem and found this solution which works for me:

App.config:

<configuration>
<connectionStrings>
    <add name="Default" 
        connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=EFCoreSample;Integrated Security=SSPI"/>
</connectionStrings>

Add a constructor to the DB context which receives a DbContextOptions and passes it to the base constructor:

public class EFCoreSampleDbContext : DbContext
{
    public EFCoreSampleDbContext(DbContextOptions options)
        : base(options)
    {

    }
}

Define a design-time DB context factory, which uses ConfigurationBuilder to retrieve the connection string from the App.config. When you run a command such as add-migration, EF Core looks for a class inside the project which implements the IDesignTimeDbContextFactory to receive an instance of the DB context, and if not found, instantiates the Db context by itself.

You can find more about the design-time DB context factory here:

https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-a-design-time-factory[2]

You need to add Nuget packages Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.FileExtensions, and Microsoft.Extensions.Configuration.Xml for this one. Also, set the "Copy to Output Directory" property of the App.config file to "Copy always".

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<EFCoreSampleDbContext>
{
    public EFCoreSampleDbContext CreateDbContext(string[] args)
    {
        var optionsBuilder =
            new DbContextOptionsBuilder<EFCoreSampleDbContext>();

        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddXmlFile("App.config")
            .Build();

        string connectionString;
        if (configuration
            .Providers
            .FirstOrDefault()
            .TryGet("connectionStrings:add:Default:connectionString",
                out connectionString))
        {

            optionsBuilder.UseSqlServer(connectionString);
            var dbContext =
                new EFCoreSampleDbContext(optionsBuilder.Options);
            return dbContext;
        }
        else
            throw new Exception("The design-time connection string not found!");
    }
}
like image 36
Mahdi Avatar answered Oct 09 '22 18:10

Mahdi