Database initialization Starting too late
EDITED
I am running a unit test (using nunit) for the databsae init method. I installed the test and started the database with a test case using this:
[TestFixtureSetUp]
public void SetUp()
{
//select database
Database.DefaultConnectionFactory = new SqlConnectionFactory(connectionString);
database = new POSDatabaseContext(connectionString);
//drop database and recreate
string query = "ALTER DATABASE [POS] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;";
database.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, query);
database.Database.Delete();
database.Database.Create();
//add seed value to test against
database.Metadata.Add(new Metadata {
ID = "META",
IsInitialized = false,
testBool = true,
TimeCreated = DateTime.Now,
TimeEdited = DateTime.Now
});
database.SaveChanges();
}
Then I run the test method:
[Test]
public void InitializeDatabaseTestWithMatchingModel()
{
//set initializer for data context to test it, and run it
Database.SetInitializer<POSDatabaseContext>(new
CustomDropCreateDatabaseWithMatchingModelTest());
database.Database.Initialize(true);
//use the metadata table to check if it was run correctly
//if metadata exist, which it should
if(database.Metadata.Any(s => s.ID == "META"))
{
Metadata actual = database.Metadata.Single(s => s.ID == "META");
Assert.IsTrue(actual.IsInitialized);
}
else
throw new Exception("The Database was not seeded correctly for the test");
}
To test my custom initializer, I created a class that inherits from it, I could use the Seed () method to do some data that I could check. This is the class:
//class is derived from class that needs to be tested so can still be used for testing
//this class adds seed data to check the database with
public class CustomDropCreateDatabaseWithMatchingModelTest
: CustomDropCreateDatabaseIfModelChanges<POSDatabaseContext>
{
protected override void Seed(POSDatabaseContext context)
{
//if metadata exist (Which it should since the database
//shouldn't be dropped because the model is the same)
if(context.Metadata.Any(s => s.ID == "META"))
{
Metadata meta = context.Metadata.Single(s => s.ID == "META");
if(meta.IsInitialized == true)
throw new Exception("The database has not been dropped"+
" and recreated correctly during the unit test setup."
);
else
{
meta.IsInitialized = true;
meta.TimeEdited = DateTime.Now;
context.SaveChanges();
}
}
else
throw new Exception("Metadata not found. The database was"+
" either dropped because it was falsely accused of not"+
" having a matching model, OR the database was not seeded"+
" properly during unit test setup."
);
}
}
And the class it was derived from:
//This file contains custom versions of the built in
//database intializer classes in which an SQL Statement
//is run to stop all connections to the database so that
//when the database is dropped, a database in use
//exception is not thrown.
public class CustomDropCreateDatabaseIfModelChanges<Context>
: IDatabaseInitializer<Context> where Context : DbContext
{
public void InitializeDatabase(Context context)
{
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(true))
{
string query = "ALTER DATABASE [POS] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;";
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, query);
context.Database.Delete();
context.Database.Create();
}
//else do nothing and continue
}
else context.Database.Create();
//run seed method
Seed(context);
}
//OVERIDE ME
//this method seeds the database with data
protected virtual void Seed(Context context) { }
}
The problem I seem to be running into is that initialization doesn't take place until it is asserted. I checked the metadata values and the values are the values of the SetUp () method. I have also verified that the object is marked as modified before SaveChanges () is called in the method I am testing. However, the method certainly works because the database reflects the values from the method.
The original problem I was thinking was that the IsInitialized property was returning as an invalid value since it was 1 in the database, but I got false in the code.
source to share
Change Assert.AreEqual(true,
to Assert.IsTrue(
because it true
can have different shapes between types. As you noticed, it was encoded 1
in the database, which may not match exactly.
You can check if the context updates correctly before the assertion SaveChanges()
: do the assertion " TimeEdited
" first. (Replace DateTime.Now with some constant value that is easier to compare first).
source to share
At the top of my test class, I:
[TestFixture]
public class CustomDropCreateDatabaseIfModelChangesTest
{
protected const string connectionString =
"Data Source=SABERTOOTH\\POS;Initial Catalog=POS;Integrated Security=true;Pooling=false";
protected POSDatabaseContext database;
....
}
I modified the test to use the new DbContext like so:
[Test]
public void InitializeDatabaseTestWithMatchingModel()
{
//set initializer for data context to test it, and run it
Database.SetInitializer<POSDatabaseContext>(new CustomDropCreateDatabaseWithMatchingModelTest());
database.Database.Initialize(true);
POSDatabaseContext newContext = new POSDatabaseContext(connectionString);
//use the metata table to check if it was run correctly
//if metadata exist, which it should
if(newContext.Metadata.Any(s => s.ID == "META"))
{
Metadata actual = newContext.Metadata.Single(s => s.ID == "META");
Assert.IsTrue(actual.IsInitialized);
}
else
throw new Exception("The Database was not seeded correctly");
}
So it turns out that Pieter21 was right, the context was not updated for some reason and a new context needed to be created. However, I don't know why.
source to share