Resolving connection string in EFv6.3 in ASP.NET Core Windows service project

asp.net-core entity-framework entity-framework-6

Question

I have an ASP.NET Core application running as a Windows Service. Due to project requirements, I am using Entity Framework v6.3 (as opposed to using EF Core).

I am having trouble retrieving the correct connection string when performing a migration.

As I understand it, Entity Framework 6 is supposed to retrieve connection strings from web.config or app.config files, right? Therefore, I have an app.config file containing two connection strings, e.g.

<connectionStrings>
    <add name="DataContext" providerName="System.Data.SqlClient" connectionString="Server=localhost\SQLEXPRESS;[redacted]" />
    <add name="CrmContext" providerName="System.Data.SqlClient" connectionString="Server=localhost\SQLEXPRESS;[redacted]" />
</connectionStrings>

In my Startup class, I have registered both database contexts, e.g.

services.AddTransient<DataContext>();
services.AddTransient<CrmContext>();

On a Razor page, when I instantiate both data contexts I can see the correct connection string is being resolved for both contexts (by using _crmContext.Database.Connection.ConnectionString).

So far so good.

My DataContext is an existing database in a separate project, but my CrmContext is a new database that I want to add a migration to. I have enabled automatic migrations but when running the update-database command it doesn't find my connection string and instead defaults to creating a LocalDB database at this connection string: (localdb)\MSSQLLocalDB.

This isn't what I want, because the CrmContext database is actually an existing database that I am mocking up for now, so I will soon need it to resolve the correct connection string. How come it isn't using the connection string I have provided in my app.config file?

CrmContext.cs

public class CrmContext : DbContext
{
    public CrmContext() : base("CrmContext")
    {
        Database.SetInitializer<CrmContext>(null);
    }

    public IDbSet<CustomerDetails> CustomerDetails { get; set; }
}

CrmConfiguration.cs

internal sealed class CrmConfiguration : DbMigrationsConfiguration<CrmContext>
{
    public CrmConfiguration()
    {
        AutomaticMigrationsEnabled = true;
    }

    protected override void Seed(CrmContext context)
    {
        ...

        context.SaveChanges();
    }
}

I've tried to explicitly update the CRM connection by specifying my CrmConfiguration file:

update-database -ConfigurationTypeName CrmContext

When I do this, it creates and updates the LocalDB database instead of using my connection string.

I've also tried to explicitly referencing the connection string:

update-database -ConnectionStringName "CrmContext"

This results in this error:

No connection string named 'CrmContext' could be found in the application config file.

To clarify, my CrmContext and CrmContext classes exist in the my current ASP.NET Core windows service project, and when I am invoking the update commands I am making sure to select that project, e.g.

enter image description here

enter image description here

I know from using Entity Framework Core that you have to manually provide the connection string for EF Core:

services.AddDbContext<DataContext>(options => options.UseSqlServer(connectionString....

No such extension method exists in EFv6.3, but I would have thought it resolved connection strings the usual way by picking them up from the app.config file? And why the inconsistency between the migration connection string and the connection string that gets resolved when running the web application? Any ideas what I'm doing wrong?

It does work when I directly supply the connection string on the context:

public CrmContext() 
    : base("Server=localhost\\SQLEXPRESS;[redacted]")

This can do for now, but I would much rather I could configure this without having to recompile my service, and without any risk of a UAT/production connection string inadvertently being committed to source control.

When I publish my service I found that it doesn't pick up the connection strings at all from any of the config files - it again just looks for the default localdb database. I've tried inserting the connection string into the following places without success: - app.config - web.config - appsettings.json

My app.config file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="DataContext" providerName="System.Data.SqlClient" connectionString="redacted" />
    <add name="CrmContext" providerName="System.Data.SqlClient" connectionString="redacted" />
  </connectionStrings>
</configuration>

enter image description here

As per Microsoft documentation, it should be possible to load in the additional XML configuration files via the following code in the Program.cs file, but EntityFramework still didn't pick up the connection strings:

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var workingDirectoryPath = Environment.GetEnvironmentVariable(EnvironmentVariables.ServiceWorkingDirectory);

        config.AddXmlFile(Path.Combine(workingDirectoryPath, "app.config"));
        config.AddXmlFile(Path.Combine(workingDirectoryPath, "web.config"));
    })

Thanks for reading this far!

1
0
3/6/2020 12:16:39 PM

Popular Answer

When you deploy your service, the .config file should instead be called servicename.exe.config. It should reside on the same folder where the service was registered with installutil. See Windows service app.config location.

0
2/17/2020 11:18:04 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow