Debugging Code Called by EF Core Add-Migrations

entity-framework-core entity-framework-core-migrations

Question

I have an Entity Framework Core database defined in a separate assembly, using the IDesignTimeDbContextFactory<> pattern (i.e., I define a class, derived from IDesignTimeDbContextFactory, which has a method called CreateDbContext that returns an instance of the database context).

Because the application of which the EF Core database is a part utilizes AutoFac dependency injection, the IDesignTimeDbContextFactory<> factory class creates an AutoFac container in its constructor, and then resolves the DbContextOptionsBuilder<>-derived class which is fed into the constructor for the database context (I do this so I can control whether a local or an Azure-based SqlServer database is targeted, based on a config file setting, with passwords stored in an Azure KeyVault):

public class TemporaryDbContextFactory : IDesignTimeDbContextFactory<FitchTrustContext>
{
    private readonly FitchTrustDBOptions _dbOptions;

    public TemporaryDbContextFactory()
    {
        // OMG, I would >>never<< have thought to do this to eliminate the default logging by this
        // deeply-buried package. Thanx to Bruce Chen via 
        // https://stackoverflow.com/questions/47982194/suppressing-console-logging-by-azure-keyvault/48016958#48016958
        LoggerCallbackHandler.UseDefaultLogging = false;

        var builder = new ContainerBuilder();

        builder.RegisterModule<SerilogModule>();
        builder.RegisterModule<KeyVaultModule>();
        builder.RegisterModule<ConfigurationModule>();
        builder.RegisterModule<FitchTrustDbModule>();

        var container = builder.Build();

        _dbOptions = container.Resolve<FitchTrustDBOptions>() ??
                     throw new NullReferenceException(
                         $"Could not resolve {typeof(FitchTrustDBOptions).Name}");
    }

    public FitchTrustContext CreateDbContext( string[] args )
    {
        return new FitchTrustContext( _dbOptions );
    }
}

public class FitchTrustDBOptions : DbContextOptionsBuilder<FitchTrustContext>
{
    public FitchTrustDBOptions(IFitchTrustNGConfigurationFactory configFactory, IKeyVaultManager kvMgr)
    {
        if (configFactory == null)
            throw new NullReferenceException(nameof(configFactory));

        if (kvMgr == null)
            throw new NullReferenceException(nameof(kvMgr));

        var scannerConfig = configFactory.GetFromDisk()
                            ?? throw new NullReferenceException(
                                "Could not retrieve ScannerConfiguration from disk");

        var dbConnection = scannerConfig.Database.Connections
                               .SingleOrDefault(c =>
                                   c.Location.Equals(scannerConfig.Database.Location,
                                       StringComparison.OrdinalIgnoreCase))
                           ?? throw new ArgumentOutOfRangeException(
                               $"Cannot find database connection information for location '{scannerConfig.Database.Location}'");

        var temp = kvMgr.GetSecret($"DatabaseCredentials--{dbConnection.Location}--Password");

        var connString = String.IsNullOrEmpty(dbConnection.UserID) || String.IsNullOrEmpty(temp)
            ? dbConnection.ConnectionString
            : $"{dbConnection.ConnectionString}; User ID={dbConnection.UserID}; Password={temp}";

        this.UseSqlServer(connString,
            optionsBuilder =>
                optionsBuilder.MigrationsAssembly(typeof(FitchTrustContext).GetTypeInfo().Assembly.GetName()
                    .Name));
    }
}

Needless to say, while this provides me with a lot of flexibility (I can switch from local to cloud database just by changing a single config parameter, and any required passwords are reasonably securely stored in the cloud), it can trip up the add-migration commandlet if there's a bug in the code (e.g., the wrong name of a configuration file).

To debug those kinds of problems, I've often had to resort to outputting messages to the Visual Studio output window via diagnostic WriteLine calls. That strikes me as pretty primitive (not to mention time-consuming).

Is there a way to attach a debugger to my code that's called by add-migration so I can step thru it, check values, etc? I tried inserting a Launch() debugger line in my code, but it doesn't work. It seems to throw me into add-manager codebase, for which I have no symbols loaded, and breakpoints that I try to set in my code show up as the empty red circle: they'll never be hit.

Thoughts and suggestions would be most welcome!

1
2
3/15/2018 10:55:46 PM

Popular Answer

Add Debugger.Launch() to the beginning of the constructor to launch the just-in-time debugger. This lets you attach VS to the process and debug it like normal.

2
3/16/2018 3:47:40 PM


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