I'm trying to migrate a database that is only known at runtime, which means that I cannot use the Package Manager Console to update the database. Also it's not just one, but many databases, but it is the same schema for all of them :)
I'm using Ninject to generate and inject the connection string on the DbContext object. The context constructor looks just like this:
public class MyEntities : DbContext
{
public MyEntities(string database) : base(database) { }
/* Properties code omitted. */
}
The method to instantiate a context follows:
public MyEntities GetDatabase(string databaseName, string connectionString)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
builder.InitialCatalog = databaseName;
MyEntities context = this._kernel.Get<MyEntities>(new ConstructorArgument(
"database", builder.ConnectionString));
Database.SetInitializer<MyEntities>(
new MigrateDatabaseToLatestVersion<MyEntities, MyEntitiesMigrationConfiguration>("MyEntities"));
return context;
}
When a context is retrieved the method creates a connection string and passes it as parameter to the constructor of MyEntities. Also I specify in the method the type of migration I want (in this case MigrateDatabaseToLatestVersion).
The migration code follows:
public partial class MyAccountInMonth : DbMigration
{
public override void Up()
{
AlterColumn("AccountsInMonths", "MovementDebtMonth", c => c.Long(nullable: false));
AlterColumn("AccountsInMonths", "MovementCreditMonth", c => c.Long(nullable: false));
AlterColumn("AccountsInMonths", "BalanceMonth", c => c.Long(nullable: false));
AlterColumn("AccountsInMonths", "MovementDebtAccumulated", c => c.Long(nullable: false));
AlterColumn("AccountsInMonths", "MovementCreditAccumulated", c => c.Long(nullable: false));
AlterColumn("AccountsInMonths", "BalanceAccumulated", c => c.Long(nullable: false));
}
public override void Down() { /* Code omitted */ }
}
When I run the application, the following error occurs:
Cannot find the object "AccountsInMonths" because it does not exist or you do not have permissions.
What the migration does is change the type of the column AccountsInMonths from int to long.
It is a migration error, because the stack trace has calls to it. At this moment I can only think that the problem can be permissions because the table exists. Other possibility is some kind of problem on the connection string. Please, anybody has a clue for this? Thanks in advance!
PS: if it is not clear I can add more information to the question.
I Had the several issue. Here is the solution:
1) Make your Configuration as a public class:
public sealed class Configuration : DbMigrationsConfiguration<YourContextClassHere>
2) Make your Seed() method as a public
2) Add anywhere the code below, this will apply the latest migration and updates your db:
Configuration configuration = new Configuration();
configuration.ContextType = typeof(YourContextClassHere);
var migrator = new DbMigrator(configuration);
// This will update the schema of the DB
migrator.Update();
// This will run Seed() method
configuration.Seed(new YourContextClassHere());
To migrate a database outside of Visual Studio and the Package Manager Console, use the accompanying migrate.exe tool that is located in the EntityFramework nuget package. It basically let you do the same migration from a command line.
You can use the Package Manager Console with Update-Database. It has a switch to specify connection string - either connection string itself ($ConnectionString and $ConnectionProviderName) or named one from config ($ConnectionStringName).
Or you can use DbMigrator class to handle it yourself in code (similar to what's actually migrate.exe doing).
Related
What is the right way to work with multiple providers?
My Example:
appsettings.json
{
"ConnectionStrings": {
"Sqlite": "Data Source=database.db"
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("Sqlite")));
}
DatabaseContext.cs
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
public DbSet<TestModel> TestModel{ get; set; }
}
A easy way for multiple providers?
A solution with only one Context (Example for SQLite + MySQL + MSSQL + PostgreSQL (or others)):
appsettings.json
{
// Add Provider and ConnectionStrings for your EFC drivers
// Providers: SQLite, MySQL, MSSQL, PostgreSQL, or other provider...
"Provider": "SQLite",
"ConnectionStrings": {
"SQLite": "Data Source=mydatabase.db",
"MySQL": "server=localhost;port=3306;database=mydatabase;user=root;password=root",
"MSSQL": "Server=(localdb)\\mssqllocaldb;Database=mydatabase;Trusted_Connection=True;MultipleActiveResultSets=true",
"PostgreSQL": "Host=localhost;Database=mydatabase;Username=root;Password=root"
}
}
Single DatabaseContext.cs
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
// add Models...
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Check Provider and get ConnectionString
if (Configuration["Provider"] == "SQLite")
{
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("SQLite")));
}
else if (Configuration["Provider"] == "MySQL")
{
services.AddDbContext<DatabaseContext>(options =>
options.UseMySql(Configuration.GetConnectionString("MySQL")));
}
else if (Configuration["Provider"] == "MSSQL")
{
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MSSQL")));
}
else if (Configuration["Provider"] == "PostgreSQL")
{
services.AddDbContext<DatabaseContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("PostgreSQL")));
}
// Exception
else
{ throw new ArgumentException("Not a valid database type"); }
}
Now we can do a singel migration
Add-Migration InitialCreate
Only edit every output of Add-Migration and add driver specific attributes:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Mytable",
columns: table => new
{
Id = table.Column<int>(nullable: false)
// Add for SQLite
.Annotation("Sqlite:Autoincrement", true)
// Add for MySQL
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn)
// Add for MSSQL
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn)
// Add for PostgreSQL
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
// Or other provider...
Name = table.Column<string>(maxLength: 50, nullable: false),
Text = table.Column<string>(maxLength: 100, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Mytable", x => x.Id);
});
}
EDIT:
or you use string ID "DatabaseGenerated"
so you would not have to edit migrationBuilder and the add migration is multiple providers capable without ".Annotation"
EXAMPLE Model:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WebApplication.Models
{
public class Mytable
{
// This generate a String ID
// No ID modification needed for providers
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
// ....
}
}
Now ready for Update-Database
You may want to consider a utility like AdaptiveClient. AdaptiveClient allows you to create a single DbContext with multiple provider-specific implementations of your services (MSSQL, MySQL, SQLite, etc). AdaptiveClient injects the correct implementation based on the connection string in use.
AdaptiveClient also allows you to inject transport-specific service implementations. For example many applications run both locally (same LAN as database server) and remotely (use WCF or REST). When running locally AdaptiveClient will inject an implementation of your service that talks directly to your database. This gives a ~10x performance improvement. When running remotely AdaptiveClient injects a WCF or REST implementation.
See also:
AdaptiveClient.EntityFrameworkCore
Demo Application
AdaptiveClient is available as a nuget package.
Disclaimer: I am the author of AdaptiveClient.
Seimann's answer is good but I found working with migrations to be a pain. I wanted little or no manual work to get it working. I found the easiest way was to create a separate assembly for each provider and add an implementation of IDesignTimeDbContextFactory.
Another solution is to create a design time assembly but selecting which provider to use for migrations turned out to be difficult, at least until this feature is implemented here. I tried the suggested method of setting an environment variable before executing the migrations but I found using compiler constants to select the correct provider to be easier.
I organized this by creating a shared project to be used by all providers. Here is an example implementation that pulls your main projects configuration settings. This class will support both methods explained above so it can be simplified depending on your needs.
#if DEBUG
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
namespace Database.DesignTime
{
public class ApplicationDbContextDesignTimeFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Path.GetFullPath(#"..\MainProjectDirectory"))
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.Development.json")
.Build();
// Determine provider from environment variable or use compiler constants below
var databaseProvider = Environment.GetEnvironmentVariable("DatabaseProvider");
#if SQLSERVER
databaseProvider = "SqlServer";
#endif
#if POSTGRESQL
databaseProvider = "PostgreSql";
#endif
var connectionString = configuration.GetConnectionString($"{databaseProvider}Connection");
var contextBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
switch (databaseProvider)
{
#if SQLSERVER
case "SqlServer":
contextBuilder.UseSqlServer(connectionString, dbOptions =>
{
dbOptions.MigrationsAssembly("Database.SqlServer");
});
break;
#endif
#if POSTGRESQL
case "PostgreSql":
contextBuilder.UseNpgsql(connectionString, dbOptions =>
{
dbOptions.MigrationsAssembly("Database.PostgreSql");
});
break;
#endif
default:
throw new NotSupportedException(databaseProvider);
}
return new ApplicationDbContext(contextBuilder.Options);
}
}
}
#endif
Then in your database migration project add the compiler constant for each provider. For example:
Database.SqlServer.csproj
<DefineConstants>SQLSERVER</DefineConstants>
Database.PostgreSql.csproj
<DefineConstants>POSTGRESQL</DefineConstants>
When you want to add migrations from within VS, open the Package Manager Console and select the migration project as the Default project. When executing the command, you need to specify the project containing the implementation of IDesignTimeDbContextFactory you want to use.
Add-Migration Initial -StartupProject "Database.SqlServer"
Now you can switch back to your main project and use it as normal. Just for reference this is my relevant appsettings.json and startup code.
{
"DatabaseProvider": "SqlServer",
"ConnectionStrings": {
"SqlServerConnection": "Server=(localdb)\\mssqllocaldb;Database=DatabaseName;Trusted_Connection=True;MultipleActiveResultSets=true",
"PostgreSqlConnection": "Host=host;Database=DatabaseName;User ID=Test;Password=secrectPass"
}
services.AddDbContext<ApplicationDbContext>(options =>
{
switch (Configuration["DatabaseProvider"])
{
case "SqlServer":
options.UseSqlServer(Configuration.GetConnectionString("SqlServerConnection"), dbOptions =>
{
dbOptions.MigrationsAssembly("Database.SqlServer");
});
break;
case "PostgreSql":
options.UseNpgsql(Configuration.GetConnectionString("PostgreSqlConnection"), dbOptions =>
{
dbOptions.MigrationsAssembly("Database.PostgreSql");
});
break;
}
});
There is another suggested way to accomplish this as explained here but I found creating derived classes means the migration will only work for instances of the derived class and not the base class. So you would need to specify the derived class type in AddDbContext. The other method mentioned requires manual work which I want to avoid.
I'm having a problem to handle the DB connection string in conjunction with migrations.
I have 2 projects:
Domain
Application
The DbContext is in the Domain project, so this is the project I run migrations against.
The migrations concept enforces me to implement OnConfiguring in my DbContext and therein specify the database provider, eg:
protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
builder.UseSqlServer("<connection string>");
}
My problem is that I don't want to use a hard coded connection string, for obvious reasons, and I cannot use ConfigurationManager to read it from the config file since the config file is in the application project.
All the examples I've seen involve either hard-coding the connection string or putting it in my ASP.NET Core application's settings files.
If you aren't using ASP.NET Core, or maybe, I don't know, don't want to have your local environment's database details committed to source control, you can try using a temporary environment variable.
First, implement IDesignTimeDbContextFactory like this (note that IDbContextFactory is now deprecated):
public class AppContextFactory: IDesignTimeDbContextFactory<AppContext>
{
public AppContextFactory()
{
// A parameter-less constructor is required by the EF Core CLI tools.
}
public AppContext CreateDbContext(string[] args)
{
var connectionString = Environment.GetEnvironmentVariable("EFCORETOOLSDB");
if (string.IsNullOrEmpty(connectionString))
throw new InvalidOperationException("The connection string was not set " +
"in the 'EFCORETOOLSDB' environment variable.");
var options = new DbContextOptionsBuilder<AppContext>()
.UseSqlServer(connectionString)
.Options;
return new AppContext(options);
}
}
Then, you can include the environment variable when you call Update-Database, or any of the other EF Core tools:
$env:EFCORETOOLSDB = "Data Source=(local);Initial Catalog=ApplicationDb;Integrated Security=True"; Update-Database
Here's how I do it, without a lot of extra code or craziness.
Project Structure:
AspNetCoreProject.Web
AspNetCoreProject.Data <-- DbContext here
My DbContext is set up with the constructor that allows you to inject the DbContextOptions
AspNetCoreProject.Data
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}
}
In your application or web application, you set up your ConfigureServices normally.
AspNetCoreProject.Web / Startup.cs / ConfigureServices()
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("connection"))
Now, what about migrations? Well, I "trick" the Visual Studio UI into working as expected.
First, make sure your application (AspNetCoreProject.Web project with Startup.cs) is the start up project.
Second, open up your Nuget Package Manager Console. At the top of the Nuget PM> Console, there's a dropdown for 'Set Default Project', point this to your AspNetCoreProject.Data or project with the DbContext class.
Run your migration commands normally. add-migration init then update-database
Assuming your DbContext class has a constructor that accepts a parameter of type DbContextOptions, the dotnet ef commands have native support for this scenario - requiring no code changes nor additional configuration. Just use the "--startup-project" and "--project" parameters when creating and running migrations.
For example, let's say you have a "Application" project with your configuration and a separate project called "Domain" where the DbContext is implemented.
Context:
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}
}
Startup:
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("connection"))
CLI command:
dotnet ef database update --startup-project Application --project Domain
We've had a same issue and there is a solution. :)
You have to implement IDbContextFactory<TContext>
When doing so you can read the connectionstrings from your appsettings.json. You can also use Add-Migration without errors, because overwriting OnConfigure() is obsolete then.
Sample implementation:
public class DomainContextFactory : IDbContextFactory<DomainContext>
{
public string BasePath { get; protected set; }
public DomainContext Create()
{
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var basePath = AppContext.BaseDirectory;
return Create(basePath, environmentName);
}
public DomainContext Create(DbContextFactoryOptions options)
=> Create(options.ContentRootPath, options.EnvironmentName);
private DomainContext Create(string basePath, string environmentName)
{
BasePath = basePath;
var configuration = Configuration(basePath, environmentName);
var connectionString = ConnectionString(configuration.Build());
return Create(connectionString);
}
private DomainContext Create(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
{
throw new ArgumentException($"{nameof(connectionString)} is null or empty", nameof(connectionString));
}
var optionsBuilder = new DbContextOptionsBuilder<DomainContext>();
return Configure(connectionString, optionsBuilder);
}
protected virtual IConfigurationBuilder Configuration(string basePath, string environmentName)
{
var builder = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("constr.json")
.AddJsonFile($"constr.{environmentName}.json", true)
.AddEnvironmentVariables();
return builder;
}
protected virtual string ConnectionString(IConfigurationRoot configuration)
{
string connectionString = configuration["ConnectionStrings:DefaultConnection"];
return connectionString;
}
protected virtual DomainContext Configure(string connectionString, DbContextOptionsBuilder<DomainContext> builder)
{
builder.UseSqlServer(connectionString, opt => opt.UseRowNumberForPaging());
DomainContext db = new DomainContext(builder.Options);
return db;
}
DomainContext IDbContextFactory<DomainContext>.Create(DbContextFactoryOptions options)
=> Create(options.ContentRootPath, options.EnvironmentName);
}
How we use it:
public override IServiceResult<IList<Datei>> LoadAllData()
{
using (var db = this.DomainContextFactory.Create())
{
var files = db.Datei
.ToListAsync<Datei>();
return new ServiceResult<IList<Datei>>(files.Result, files.Result.Count);
}
}
sample config
{
"ConnectionStrings": {
"DefaultConnection": "Put your connectionstring here"
}
}
I was using OnConfiguring below with configured in Windows environment variable MsSql.ConnectionString and command for initial ef migration creation started to work: dotnet ef migrations add InitialCreate
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = Environment.GetEnvironmentVariable("MsSql.ConnectionString");
if(string.IsNullOrEmpty(connectionString))
throw new ConfigurationErrorsException("Sql server connection string configuration required");
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseSqlServer(connectionString)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}
}
To configure environment variable:
use Win + R hotkeys kombination to open Run command window
Type systempropertiesadvanced and hit Enter
On Advanced tab click Environment Variables
Click New... button
In Variable name field type MsSql.ConnectionString
In Variable value field type your connection string value
Make sure console(and any program that starts console) is restarted after new variable addition and before running dotnet ef related commands
I have my DBContext in my console app and was using a ctor with few parameters (such as connection string etc), since EF Core Migrations was using the default parameter less ctor and hence the connection string wasn't being populated I had the migrations failing.
Just added code to get the connection string from ConfigurationBuilder within my default ctor to by pass this.
Was only playing around with console app and EF Core so this works for me for now.
I'm trying to create an schema independent model with EntityFramework Codefirst and an Oracle database but EF uses as defaults for migrations dbo as schema.
I overridden OnModelCreating method on my DBContext to solve this and use the user in the connectionString instead
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema(string.Empty);
}
The problem is that __MigrationHistory ignores this default schema and I get this error when running first migration:
ORA-01918: User 'dbo' does not exist
Tried this msdn entry to customize the schema for this table.
CustomHistoryContext:
public class CustomHistoryContext : HistoryContext
{
public CustomHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema) {}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema(String.Empty);
}
}
And DBConfiguration:
public sealed class Configuration :
DbMigrationsConfiguration<Model.MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(connection, defaultSchema) => new CustomHistoryContext(connection, defaultSchema));
}
protected override void Seed(Model.Model1 context)
{
}
}
And is working fine for the first migration. But when I modify my entity model and try to reflect this change with add-migration command I get the following error:
Unable to generate an explicit migration because the following
explicit migrations are pending: [201706281804589_initial,
201706281810218_pp2]. Apply the pending explicit migrations before
attempting to generate a new explicit migration.
Looks like EF gets lost and can't find migrations history at this point.
When I comment the SetHistoryContextFactory instruction in Configuration it works for subsequent add-migration commands but this workaround isn't enough for scenarios when I want to run all migrations from scratch like deploying.
Does anyone knows if I'm in the good way to accomplish this or if there is a better workaround for this?
Go to Migrations -> Configuration.cs and below mentioned code. This fix worked for me!
class Configuration : DbMigrationsConfiguration<CodeFirstOracleProject.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
var historyContextFactory = GetHistoryContextFactory("Oracle.ManagedDataAccess.Client");
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(dbc, schema) => historyContextFactory.Invoke(dbc, "YourSchemaName"));
}
}
Try runnig Enable-Migrations -force again. Then run Add-Migration SomeDescription –IgnoreChanges. After that, run "Update-Database - Verbose". This worked to me
I successfully tried the following inside the Configuration : DbMigrationsConfiguration class for Oracle, in order to change the history schema to "Test":
var historyContextFactory = GetHistoryContextFactory("Oracle.ManagedDataAccess.Client");
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(dbc, schema) => historyContextFactory.Invoke(dbc, "Test"));
So basically, instead of trying to register a custom history context with unchanged default schema, I tried to register the default history context with changed default schema.
The result: when I run Update-Database -Script, the resulting script contains the new schema for creation of the __MigrationHistory table as well as for inserting the new history values:
create table "Test"."__MigrationHistory"
-- ...
insert into "Test"."__MigrationHistory"("MigrationId", "ContextKey", "Model", "ProductVersion") ...
However, lets be perfectly clear: I just tried what I expected to work by intuition and it did work for me. I didn't find any reliable documentation to support this solution.
open Migrations -> Configuration.cs and add
public Configuration()
{
AutomaticMigrationsEnabled = false;
var historyContextFactory = GetHistoryContextFactory("Oracle.ManagedDataAccess.Client");
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(dbc, schema) => historyContextFactory.Invoke(dbc, "your_schema_name_hear"));
}
i have three tier application in Asp.net Mvc Core and use EF core,
now i want create auto migration ,
i have DAL layer that my context available here
public class AdminContext : DbContext
{
public AdminContext(DbContextOptions<AdminContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AdsAdsCategory>()
.HasKey(bc => new { bc.AdsId, bc.AdsCategoryId });
modelBuilder.Entity<AdsAdsCategory>()
.HasOne(bc => bc.Ads)
.WithMany(b => b.AdsAdsCategories)
.HasForeignKey(bc => bc.AdsId);
modelBuilder.Entity<AdsAdsCategory>()
.HasOne(bc => bc.Category)
.WithMany(c => c.AdsAdsCategories)
.HasForeignKey(bc => bc.AdsCategoryId);
}
public DbSet<Ads> Adses { get; set; }
public DbSet<AdsCategory> AdsCategories { get; set; }
public DbSet<AdsPosition> AdsPositions { get; set; }
public DbSet<AdsCustomer> AdsCustomers { get; set; }
}
and in my application startup
i write this code
var context = app.ApplicationServices.GetService<AdminContext>();
if (!context.Database.EnsureCreated())
context.Database.Migrate();
when i run application database was created and table generate but __migrationhistory doesn't exist and migration not generate,
when in start up i remove this line code
if (!context.Database.EnsureCreated())
database was created and __migrationhistory table generated,but my model table not generate,
how i can solve this problem ?
and run auto migration in EF Core in three tier application?
You need to do the following to enable Migration in MVC .NET Core.
1-open the Package Manager Console in Visual Studio. Type and execute this code.
add-migration ClassName
pm> add-migration FirstInitialize
2-After executing the code, the migration classes will be created for your models
public partial class FirstInitialize : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
//After executing the code, this section will be automatically generated for your models
}
}
3-Then, with the following code you enter in the class section of the program.cs main method, your models will be built into a database.
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<YouDbContext>();
context.Database.Migrate();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the atabase.");
}
}
4-Each time you change your models or add a new one, you have to repeat the steps. Choose a new name for your migration every time.
Sample:
pm> add-migration SecondInitialize
*I can't speak english well
Automatic migration like in EF6 do not exist in EF core. You either have to generate your migrations before starting and then use
context.Database.Migrate();
or you drop your whole database on each launch and use
context.Database.EnsureCreated();
to recreate the updated database.
The second one wont allow you to add any migrations later on, so you have to recreate entire database each time. To delete database you can use
context.Database.EnsureDeleted();
I am developing an application with WPF and Entity Framework CodeFirst.
One requirement is that the user could create separate database file per new project.
DbContext :
public class TestContext : DbContext, IUnitOfWork
{
public TestContext()
: base("TestContext")
{
}
#region Implementation of IUnitOfWork
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
#endregion Implementation of IUnitOfWork
public DbSet<Destination> Destinations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DestinationMap());
}
Connection string defined in app config :
<connectionStrings>
<clear />
<add name="TestContext" providerName="System.Data.SqlClient" connectionString="Server=.\sqlexpress;Database=databaseName;Integrated Security=True;Connect Timeout=300;MultipleActiveResultSets=true;" />
</connectionStrings>
I've used StructureMap to inject DbContext in IUnitOfWork interface to implement context per request pattern.
This is StructureMap config :
ObjectFactory.Initialize(x => x.Scan(scanner =>
{
x.For<IUnitOfWork>().CacheBy(InstanceScope.Hybrid).Use<SpitfireContext>();
}));
When user creates new project I try to change connection string and force application create database for new project.
public static void ChangeDatabase(Guid guid)
{
var sqlConnectionStringBuilder =
new SqlConnectionStringBuilder(ConfigHelper.ActiveConnection);
sqlConnectionStringBuilder["Database"] = guid.ToString();
ConfigHelper.ActiveConnection = sqlConnectionStringBuilder.ToString();
Database.DefaultConnectionFactory =
new System.Data.Entity.Infrastructure.SqlConnectionFactory(ConfigHelper.ActiveConnectionString());
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<TestContext, MigrationConfiguration>());
using (var context = new TestContext())
{
context.Database.Initialize(true);
}
}
Application just creates database for first project and after that it tries migrate previews database.
I don't create DbConext object directly in my codes so I can't pass connection string to its constructor.
What do you suggest for my problem ?
What's wrong with my code ?
That's a tricky part - changing connection strings. It kind of works but has issues.
We had a lengthy discussion on that yesterday. Take a look at this (and related) post of mine - which describes some background of troubles for code based on similar approach.
I'd like to help you further if needed - but I'd need some more info...
Why do you say I don't create DbConext object directly in my codes so I can't pass connection string to its constructor ? You have the TestContext - why not pass it through there?
Also I'm not sure about setting the DefaultConnectionFactory, I'll recheck that. It's allowed to be set but it seems recommended on app startup only, not sure. Look up this link in the meantime (it's for EF6 but code is I think similar - and you can look up the source code) - http://msdn.microsoft.com/en-US/data/jj680699
We came to the conclusion that using the DbContext factory was beneficial in such cases.
Would adding a static property on your DbContext class to store the name of the database (Initial Catalog) and then changing the connecting string so that you insert the database name with string.Format work?
Then you could change the database name at any time the next time a context is made it'll be pointing to a new target.