EntityFramework Core automatic migration - asp.net-core

In my project I have 2 databases: one is my custom database and the other one is ApplicationDbContext that Microsoft Identity gives me.
In my Startup.cs I have this code:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ApplicationDbContext db, MyContext dbPSC)
{
// ...
db.Database.EnsureCreated();
db.Database.Migrate();
dbPSC.Database.EnsureCreated();
dbPSC.Database.Migrate();
}
I don't know when there is a migration to migrate, right? Then, I have 2 errors:
the migration for ApplicationDbContext raises an error every time the application starts apart from the first time
the migration for my context seems fine
What is the best practise to use? Is it necessary to call the migration for ApplicationDbContext?
Update
I have removed the Migration folder. Then, changed the Startup.cs like
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ApplicationDbContext db, MyContext dbPSC)
{
// ...
db.Database.EnsureCreated();
dbPSC.Database.EnsureCreated();
}
but when the application starts, it doesn't create any tables at all. AuditDbContext is because I use Audit.net
public class MyContext : AuditDbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
public DbSet<Message> Messages { get; set; }
public DbSet<AuditMessage> Audit_Messages { get; set; }
#region Common Tables
public DbSet<Country> Countries { get; set; }
public DbSet<AuditCountry> Audit_Countries { get; set; }
#endregion
#region Seed
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Seed();
}
#endregion
}
Update #2
I tried another solution but it doesn't work. The solution is to use RelationalDatabaseCreator like the following code
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ApplicationDbContext db, MyContext dbPSC)
{
// ...
db.Database.EnsureCreated();
dbPSC.Database.EnsureCreated();
RelationalDatabaseCreator databaseCreator =
(RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();
databaseCreator.CreateTables();
}
As the Migrate(), the first time the app runs it creates the tables but the second time it raises an error because the tables are already exist in the database.

The issue is most likely that you're calling EnsureCreated().
From the docs:
Note that this API does not use migrations to create the database. In addition, the database that is created cannot be later updated using migrations. If you are targeting a relational database and using migrations, you can use the DbContext.Database.Migrate() method to ensure the database is created and all migrations are applied.
So, it's enough to call Migrate(). But I don't blame you, because EnsureCreated() is definitely a misleading method name. It does a lot, since it also creates the DB schema that is not even based on migrations, but on the current state of the entities.
But if you don't want to use migrations for Identity context (because you don't extend those entities in any way), then the reverse is true: You don't need to call Migrate(), and calling EnsureCreated() is enough to make sure that the schema is created the first time.
Is calling Migrate() necessary?
As for what's best practice: Some developers don't like to call Migrate() at all from code, because they believe that DB schema operations like that should be more controlled (I'm sure you know that you can execute db update from CLI too). It depends on your requirements I guess.
And with respect to this specific case on having a separate database for Identity tables: They most likely will never change, and they especially won't create a migration by themselves. So I'd say that calling Migrate() on it is unnecessary, besides the fact that it can create and migrate the database if it didn't exist (so it might be useful to keep for that reason; if you're using migrations for that context to begin with).
Detecting whether migration is needed
You can use context.Database.GetPendingMigrationsAsync() to check if migration is needed. How it's done is that the DB has an EFMigrationsHistory table that stores the Id of the applied migrations, which can be compared to the migrations files in your solution.
But I think most developers don't call this GetPendingMigrationsAsync() (or the sync version, for that matter), because you can just call Migrate(), and if the DB is already updated, no harm is done.
Clarifying Migrations vs EnsureCreated
I'm not sure how much have you worked with EF, so chances are this is obvious. But Migrate() is used when you already created migrations with e.g. dotnet ef migrations add <migrationname>, which is a way to incrementally alter the database schema with a code-first approach.
But if you don't use migrations at all, you have a very simple schema that won't be needed to be altered incrementally with migrations, and you just want to make sure that the DB exists with a schema, then use only EnsureCreated(), and don't use Migrate().
The issue is that if you happen to change your entities, EnsureCreated() won't update the database; if the database already exists, it does nothing. So you'd have to call EnsureDeleted() followed by EnsureCreated() to achieve an up-to-date DB schema without migrations. Which obviously involves losing all your data. This is why migrations are useful. EnsureCreated() is mostly used for e.g. integration testing.
Btw, you can use the same DB context for your own tables and Identity; I'd hazard to say that's the 'normal' way of working with EF. Of course you could have your specific reasons for separating I suppose.

You don't need to use both db.Database.EnsureCreated(); and db.Database.Migrate();
You need to use db.Database.EnsureCreated(); when you don't have migrations enabled in your application. If you have migrations enabled then only use db.Database.Migrate(); and that would be enough.
But if you don't enable migrations then on every change you have to re create your db. Because "EnsureCreated" only verifies if the db already exists. So better to enable Migrations.
So if you don't want to change anything in ApplicationDbContext so you can use db.Database.EnsureCreated(); but if you want to change something or may be you want to add more fields in AspNetUsers etc then you should enable migrations and use db.Database.Migrate();
Btw you can also add your custom tables in ApplicationDbContext if you want.

Based on the replies you guys sent to me, my solution is to use migration. In the Startup.cs I added those lines of code (dbPSC is a DbContext)
dbPSC.Database.EnsureCreated();
if (dbPSC.Database.GetPendingMigrations().Count() > 0)
dbPSC.Database.Migrate();

Related

In Identity for Entity Framework Core 3.x, how do I find users by Email (minding normalization) when Email uniqueness is not enforced?

userManager.FindByEmailAsync(myEmail) throws an exception if there are multiple users with the same email.
I could use:
await context.ApplicationUsers
.FirstOrDefaultAsync(x => x.NormalizedEmail == myEmail.ToUpperInvariant());
That seems to work okay. But I'm not sure if ToUpperInvariant is the right way to check, because System.Text also has Normalize(). It won't matter right now since we are using SQL Server with a case-insensitive configuration, but I don't want things to break if we ever change that.
Am I normalizing in a way that is consistent with how Entity Framework does it? I tried to find the source code, but what I found doesn't use the NormalizedEmail field, so it's likely old.
The normalization is not done by the EF Core, but the UserManager class (using ILookupNormalizer service injected via constructor or set via KeyNormalizer property).
UserManager.FindByEmailAsync method does the normalization for you before calling the store method. The problem is that EF Core store method implementation uses SingleOrDefaultAsync which throws if there are duplicate normalized emails in the database.
To fix that, you could use UserManager.NormalizeEmail method to do the normalization, and then use FirstOrDefaultAsync query as in your sample:
var normalizedEmail = userManager.NormalizeEmail(myEmail);
var firstDuplicate = await userManager.Users
.FirstOrDefaultAsync(x => x.NormalizedEmail == normalizedEmail);

NuGet Update-Database is not pulling new records in Visual Studio

Novice to VisualStudio and NuGet so I'm looking for some instruction.
I'm on a team project using TFS and using NuGet to get updates for the database of this project.
One of my team members added in a property, that added a column to one of our databases, and then also added a new record to that database. Then he created a new migration for that instance.
I am trying to update this into the rest of our VisualStudio setups.
From my research so far, I ran the command Update-Database in the NuGet package manager. This updated the structure of the database and brought in the new column but failed to bring in the new record.
We have tried Update-Database -force and that came back with nothing.
I've tried searching for a solution but I'm having a hard time finding information on migrating data in the database.
Does anyone have any suggestions on how I can go about this easily for many future updates.
Firstly, welcome to SO!
This updated the structure of the database and
brought in the new column but failed to bring in the new record.
When you create a migration, EF will automatically add the structural changes but it doesn't do the same for data. Which, if you think about it, makes sense as EF has no way of knowing what records have been added, amended or removed in your database and 9 times out of 10 we wouldn't want that data being in a migration script.
If you want to include records with your migration you have two options...
1. Seed The Database
This method will be called upon each migration
internal sealed class Configuration : DbMigrationsConfiguration<WebApplication15.DatabaseContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(WebApplication15.DatabaseContext context)
{
//
// Add records into the database
//
}
}
2. Add Data to the Migration
public partial class Initial : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.People",
c => new
{
Id = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.Id);
Sql("INSERT INTO People....");
}
public override void Down()
{
DropTable("dbo.People");
}
}
We have tried Update-Database -force and that came back with nothing.
The force flag just tells EF to migrate the database regardless of whether or not it might result in data loss (i.e dropping a table that is full of records!).
Hope this helps!

Database migration using code first in mvc 4

I have created my mvc 4 application using code first and accordingly database and table also generated now i want to delete one column (from backend) of my table. so i just want to know is there any way so that changes can occur in my code automatically according to change in database.
through package manager console using migration technique
PM> enable-migrations -EnableAutomaticMigrations
in code configuration do the following
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
now when model changes do the following.
PM> update-database
Doing it through code
Use DropCreateDatabaseAlways initializer for your database. It will always recreate database during first usage of context in app domain:
Database.SetInitializer(new DropCreateDatabaseAlways<YourContextName>());
Actually if you want to seed your database, then create your own initializer, which will be inherited from DropCreateDatabaseAlways:
public class MyInitializer : DropCreateDatabaseAlways<YourContextName>
{
protected override void Seed(MagnateContext context)
{
// seed database here
}
}
And set it before first usage of context
Database.SetInitializer(new MyInitializer());
Well if you are using code first technique then remove column from your model and run migration script(google it) this will remove column from your database. But what you want is reverse which I am not sure could be done or not.

Problems creating a database model with Entity Framework Code First

I'm trying to create a database model with Code First, without creating the database itself. That is, my DBA has set up an empty database, for which I have permission to create tables, but not a whole database. However, whenever I try to use Code First to set up the model, I get the following error:
CREATE DATABASE permission denied in database 'master'.
Is this just the way Code First works, or is there some way to modify the existing database?
P.S. -- here's the connection string --
<add name="HoursDb"
connectionString="Data Source=barksql.cedarville.edu;
Initial Catalog=Hours;
persist security info=True;
User ID=hours;
password=************;
multipleactiveresultsets=True;
App=EntityFramework"
providerName="System.Data.SqlClient" />
Update
Per #devdigital's request --
Here is the context class:
using System.Data.Entity;
using ....Models;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace LibraryAdmin.DAL
{
public class HoursDb : DbContext
{
public DbSet<DaySpec> DaySpecs { get; set; }
public DbSet<WeekSpec> WeekSpecs { get; set; }
public DbSet<ExceptionHoursSet> ExceptionHoursSets { get; set; }
public DbSet<Schedule> Schedules { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
As far as mapping the database to the connection, I am assuming it is done by a convention since the HoursDb classname matches the connection string name.
It would seem that Entity Framework Code First deals with database model changes in two ways: one, it discards the old database and re-creates a new one; or two, it does not rest content until the user manually modifies it, as affirmed by #devdigital's link to EF 4.1 CF: CREATE DATABASE permission denied in database 'master'. Slauma's suggestion therein of using Database.SetInitializer(null); is good to ensure a production database is not dropped after creation, but does not create a table structure out of an empty database like I was trying to do. This being considered, my DBA granted me create access since he'd rather us not have to manually create every table, so it's not an issue anymore.
P.S. -- the database drop I mentioned came as a result of using one of the drop initialization strategies, probably DropCreateDatabaseAlways. Glad to know it was doing it's job.

NHibernate transactions randomly not rolled back

I have a suite of integration tests that run inside transactions.
Sometimes it seems that NHibernate transactions are not being correctly rolled back. I can't work out what causes this.
Here is a slightly simplified overview of the base class that these integration test fixtures run with:
public class IntegrationTestFixture
{
private TransactionScope _transactionScope;
private ConnectionScope _connectionScope;
[TestFixtureSetUp]
public virtual void TestFixtureSetUp()
{
var session = NHibernateSessionManager.SessionFactory.OpenSession();
CallSessionContext.Bind(session);
_connectionScope = new ConnectionScope();
_transactionScope = new TransactionScope();
}
[TestFixtureTearDown]
public virtual void TestFixtureTearDown()
{
_transactionScope.Dispose();
_connectionScope.Dispose();
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
session.Dispose();
}
}
A call to the TransactionScope's commit method is never made, therefore how is it possible that data still ends up in the database?
Update I never really got my head around the way NHibernate treats transactions but I found that calling Session.Flush() within a transaction would sometimes result in the data remaining in the database, even if the transaction is then rolled back. I am not sure why you can't call Flush, but then roll back. This is a pity because during integration testing you want to be able to hit the database and flush is the only way to do this sometimes.
I had an issue with using the Identity generator that sounds similar. In a nutshell, when saving an object using an identity generator, it has to write to the database in order to get the identity value and can do this outside a transaction.
Ayende has a blog post about this.