How to make use of EF CodeFirst Migrations without hardcoding nameOrConnectionString - entity-framework

How can you make EF migrations work without hardcoding a nameorconnectionstring in the base of the required parameterless constructor?
EF migrations is forcing you to add a default constructor to the custom dbcontext. In the base of the dbcontext you have to provide a nameorconnectionstring.
public class CustomDbContext : DbContextBase
{
public CustomDbContext ()
: base("theconnectionstringwedontwanttoset") //hardcoded connection string
{
}
The fact that we need to hardcode the constructor is something we cant work with since in our client applications we work without a config file and we dynamically build up the connection since we are connecting to many different databases (server, local sql compact).
After exploring the DbMigrationsConfiguration class I found a DbConnectionsInfo property named TargetDatabase which can be set in the constructor. But even this solution did not work. This is what I did:
public sealed class Configuration : DbMigrationsConfiguration<CustomDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
//Setting the TargetDatabase in the hope it will use it in the migration
TargetDatabase = new DbConnectionInfo("Server=xxx;Database=xxx;Trusted_Connection=True", "System.Data.SqlClient");
}
public class MigrationInitializer : MigrateDatabaseToLatestVersion<CustomDbContext, Configuration>
{
}
I can see that eventually the Activator.CreateInstance is used within DbMigrator and I expected from this class to use the TargetDatabase.Create...or something.
Any help or feedback is welcome.
Thanks in advance,
Wilko

it is possible yes See answers here
I also included a test program in this question.

Related

EF Core 2.0 IDesignTimeDbContextFactory implementation issues

Using EF Core 2.0 i am trying to implement IDesignTimeDbContextFactory to read connection string from the appsettings.json file. I am getting following error on call to SetBasePath
ConfigurationBuilder does not contain a definition of SetBasePath( )
public class DbContextFactory : IDesignTimeDbContextFactory<TestDbContext>
{
public TestDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<TestDbContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
return new TestDbContext(builder.Options);
}
}
Second Question : Using this approach is it necessary to use dotnet CLI, will this method be called if i am just running my migration commands using "Package Manager Console" ?
The SetBasePath() method is an extension method in FileExtensions.
You can get it by adding the Microsoft.Extensions.Configuration.FileExtensions package. I see you have AddJsonFile() also, so you might want to use Microsoft.Extensions.Configuration.Json instead since it depends on FileExtensions (i.e you'll get both).
On the 2nd question, you don't need the IDesignTimeDbContextFactory<>, but without if you'll need to set the connection string in another place that is accessible during design time. Another example is to set it in the constructor of your DbContext, but that has various design problems like if you want to run a test vs production instance.
If you don't have a factory the DbContext is created using a public parameterless constructor.

How do I implement DbContext inheritance for multiple databases in EF7 / .NET Core

I am building web APIs in ASP.NET Core 1.1.
I have a number different databases (for different systems) which have common base schemas for configuration items such as Configuration, Users and groups (about 25 tables in all). I am trying to avoid duplicating the quite extensive EF configuration for the shared part of the model by inheriting from a base class as shown in the diagram.
However, this does not work because of the Entity Framework (EF) requirement to pass DbContextOptions<DerivedRepository> as a parameter to the constructor, where DerivedRepository must match the type of the repository the constructor is called on. The parameter must then be passed down to the base DbContext by calling :base(param).
So when (for example) InvestContext is initialised with DbContextOptions<InvestContext>, it calls base(DbContextOptions<InvestContext>) and EF throws an error because the call to the ConfigurationContext constructor is receiving a parameter of type DbContextOptions<InvestContext> instead of the required type DbContextOptions<ConfigurationContext>. Since the options field on DbContext is defined as
private readonly DbContextOptions _options;
I can't see a way around this.
What is the best way to define the shared model once and use it multiple times? I guess I could create a helper function and call it from every derived context, but it's not nearly as clean or transparent as inheritance.
I would like to bring this post from the OP's GitHub issue to everyone's attention:
I was able to resolve this without a hack by providing a protected constructor that uses DbContextOptions without any type. Making the second constructor protected ensures that it will not get used by DI.
public class MainDbContext : DbContext {
public MainDbContext(DbContextOptions<MainDbContext> options)
: base(options) {
}
protected MainDbContext(DbContextOptions options)
: base(options) {
}
}
public class SubDbContext : MainDbContext {
public SubDbContext (DbContextOptions<SubDbContext> options)
: base(options) {
}
}
OK, I have got this working in a way which still uses the inheritance hierarchy, like this (using InvestContext from above as the example):
As stated, the InvestContext class receives a constructor parameter of type DbContextOptions<InvestContext>, but must pass DbContextOptions<ConfigurationContext> to it's base.
I have written a method which digs the connectionstring out of a DbContextOptions variable, and builds a DbContextOptions instance of the required type. InvestContext uses this method to convert its options parameter to the right type before calling base().
The conversion method looks like this:
protected static DbContextOptions<T> ChangeOptionsType<T>(DbContextOptions options) where T:DbContext
{
var sqlExt = options.Extensions.FirstOrDefault(e => e is SqlServerOptionsExtension);
if (sqlExt == null)
throw (new Exception("Failed to retrieve SQL connection string for base Context"));
return new DbContextOptionsBuilder<T>()
.UseSqlServer(((SqlServerOptionsExtension)sqlExt).ConnectionString)
.Options;
}
and the InvestContext constructor call changes from this:
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public InvestContext(DbContextOptions<InvestContext> options):base(ChangeOptionsType<ConfigurationContext>(options))
So far both InvestContext and ConfigurationContext work for simple queries, but it seems like a bit of a hack and possibly not something the designers of EF7 had in mind.
I am still concerned that EF is going to get itself in a knot when I try complex queries, updates etc. It appears that this is not a problem, see below)
Edit: I've logged this problem as an issue with the EF7 team here, and a team member has suggested a change to the EF Core core as follows:
"We should update the check to allow TContext to be a type that is derived from the current context type"
This would solve the problem.
After further interaction with that team member (which you can see on the issue) and some digging through the EF Core code, the approach I've outlined above looks safe and the best approach until the suggested change is implemented.
Depending on your requirements you can simply use the non type specific version of DbContextOptions.
Change these:
public ConfigurationContext(DbContextOptions<ConfigurationContext> options):base(options)
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public ConfigurationContext(DbContextOptions options):base(options)
public InvestContext(DbContextOptions options):base(options)
Then if you create your ConfigurationContext first, the classes that inherit it seem to get the same configuration. It may also depend on the order in which you initialize the different contexts.
Edit:
My working example:
public class QueryContext : DbContext
{
public QueryContext(DbContextOptions options): base(options)
{
}
}
public class CommandContext : QueryContext
{
public CommandContext(DbContextOptions options): base(options)
{
}
}
And in Startup.cs
services.AddDbContext<CommandContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<QueryContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
alternatively, in a test class:
var connectionString = "Data Source=MyDatabase;Initial Catalog=MyData;Integrated Security=SSPI;";
var serviceProvider = new ServiceCollection()
.AddDbContext<QueryContext>(options => options.UseSqlServer(connectionString))
.BuildServiceProvider();
_db = serviceProvider.GetService<QueryContext>();

Is there a way to get LinqPad to work with an EF Core context?

I am trying to figure out if there is something I am missing or some way to hack around the lack of support (yet) for Entity Framework Core DbContexts in LinqPad. I've compiled my code targeting 4.6.1 (as suggested on the LinqPad forum) and tried the "Entity Framework V7" driver, but as its name suggests, I don't believe it's up-to-date. It still asks for an app.config file or a connection string for the constructor.
Since EF Core contexts use DbContextOptions for construction rather than connection strings, I was thinking I could possibly create a constructor overload that takes a connection string, but that doesn't handle the underlying database driver. Is there a way to specify a factory for constructing the context? Any other possibilities? I'm feeling lost without LinqPad.
Latest EFCore 1.1 LINQPad driver (v1.1.1.1) can correctly use constructor that accepts string (when this option is selected in LINQPad).
So it's possible to add the following constructor:
public ApplicationDbContext(string connectionString)
: this(new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(connectionString).Options)
{ }
This will hard link this context instance to sql server provider, but at least not to connection string. And in addition your app not likely will ever try to use this constructor, EF Core never expects/promotes a ctor. that accepts string.
For additional safety you could wrap this constructor in #if DEBUG ... #endif so that it never gets to production.
The driver seems to be buggy / not updates at all. I found a way to bypass it by modifying the DbContext.
In theory, this should have worked, but it does not:
private string _connectionString;
public ApplicationDbContext(string connectionString) : base()
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (_connectionString == null)
base.OnConfiguring(optionsBuilder); // Normal operation
// We have a connection string
var dbContextOptionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(_connectionString);
base.OnConfiguring(dbContextOptionsBuilder);
}
LinqPad EF Core driver keeps looking for the parameterless constructor even if you specify "Via a constructor that accepts a string". That seems like a bug in the driver.
So then I gave it what it wanted, a parameterless contructor. I had to hardcode the connection string since IoC/appsettings.json config reader is not loaded and I don't feel like loading that separately in the DbContext. But it works and lets me test EF Core queries in LinqPad off my model.
This works fine for me:
private bool _isDebug = false;
public ApplicationDbContext() : base()
{
_isDebug = true;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!_isDebug)
base.OnConfiguring(optionsBuilder); // Normal operation
// We are in debug mode
var dbContextOptionsBuilder = new DbContextOptionsBuilder();
// Hardcoded connection string
optionsBuilder.UseSqlServer("data source=XXXX;initial catalog=XXXX;persist security info=True;user id=XXXX;password=XXXX;MultipleActiveResultSets=True;App=EntityFramework");
base.OnConfiguring(dbContextOptionsBuilder);
}
This is in addition to the existing public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } of course.
Edit: Be careful, it looks like this overrides default behavior, which may not be visible until you deploy to the server.

SetExecutionStrategy to SqlAzureExecutionStrategy with DbMigrationsConfiguration?

I saw a post today about implementing SqlAzureExecutionStrategy:
http://romiller.com/tag/sqlazureexecutionstrategy/
However, all examples I can find of this use a Configuration that inherits from DbConfiguration. My project is using EF6 Code First Migrations, and the Configuration it created inherits from DbMigrationsConfiguration. This class doesn't contain a definition for SetExecutionStrategy, and I can find no examples that actually combine SqlAzureExecutionStrategy (or any SetExecutionStrategy) with DbMigrationsConfiguration.
Can this be done?
If anyone else comes across this question, this is what we figured out:
Create a custom class that inherits from DbConfiguration (which has SetExecutionStrategy):
public class DataContextConfiguration : DbConfiguration
{
public DataContextConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
}
}
Then add this attribute to your DataContext, specifying that it is to use your custom class:
[DbConfigurationType(typeof(DataContextConfiguration))]
public class DataContext : DbContext, IDataContext
{
...
}
After more investigation, now I think the correct answer is that:
DbMigrationsConfiguration is completely separate and only configures the migration settings. That's why it doesn't inherit from or have the same options as DbConfiguration.
It is not loaded, and is irrelevant, for actual operation.
So you can (and should) declare a separate class based on DbConfiguration to configure the runtime behaviour.
I added some tracing and I saw that the first time you use a DatabaseContext in an application, it runs up the migration, and the migration configuration.
But, the first time the DatabaseContext is actually used (e.g. to load some data from the database) it will load your DbConfiguration class as well.
So I don't think there is any problem at all.

Is DBContext added automatically with EF 6 or must it be added manuall?

New to EF. Following along with DBContext by Lerman/Miller.
When I start a new project, adding EF6 (Database First), the DBContext seems to be added as a default (ie I don't have to add the DBContext separately with T4). Also, for Lazy Loading, the "virtual" needed in the class definitions also seems to be there by default (I don't have to add it like in the book). Is this what is expected?
When you use Database First approach and use EF x DbContext Generator it creates the DbContext for you automatically and set the navigation properties, virtual. If you want to disable lazy loading you can simply use following code
public class MyContext : DbContext
{
public MyContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Most probably the books you are reading are for code first development. If you use database first(especially with the designer) you don't need to make changes.