jeudi 29 janvier 2015

Repeatedly creating and deleting databases in Entity Framework

When writing some unit tests for our application, I stumbled upon some weird behaviour in EF6 (tested with 6.1 and 6.1.2): apparently it is impossible to repeatedly create and delete databases (same name/same connection string) within the same application context.


Test setup:



public class A
{
public int Id { get; set; }
public string Name { get; set; }
}

class AMap : EntityTypeConfiguration<A>
{
public AMap()
{
HasKey(a => a.Id);
Property(a => a.Name).IsRequired().IsMaxLength().HasColumnName("Name");
Property(a => a.Id).HasColumnName("ID");
}
}

public class SomeContext : DbContext
{
public SomeContext(DbConnection connection, bool ownsConnection) : base(connection, ownsConnection)
{

}

public DbSet<A> As { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new AMap());
}
}

[TestFixture]
public class BasicTest
{
private readonly HashSet<string> m_databases = new HashSet<string>();

#region SetUp/TearDown

[TestFixtureSetUp]
public void SetUp()
{
System.Data.Entity.Database.SetInitializer(
new CreateDatabaseIfNotExists<SomeContext>());
}


[TestFixtureTearDown]
public void TearDown()
{
foreach (var database in m_databases)
{
if (!string.IsNullOrWhiteSpace(database))
DeleteDatabase(database);
}
}

#endregion


[Test]
public void RepeatedCreateDeleteSameName()
{
var dbName = Guid.NewGuid().ToString();
m_databases.Add(dbName);
for (int i = 0; i < 2; i++)
{
Assert.IsTrue(CreateDatabase(dbName), "failed to create database");
Assert.IsTrue(DeleteDatabase(dbName), "failed to delete database");
}

Console.WriteLine();
}

[Test]
public void RepeatedCreateDeleteDifferentName()
{
for (int i = 0; i < 2; i++)
{
var dbName = Guid.NewGuid().ToString();
if (m_databases.Add(dbName))
{
Assert.IsTrue(CreateDatabase(dbName), "failed to create database");
Assert.IsTrue(DeleteDatabase(dbName), "failed to delete database");
}
}

Console.WriteLine();
}

[Test]
public void RepeatedCreateDeleteReuseName()
{
var testDatabases = new HashSet<string>();
for (int i = 0; i < 3; i++)
{
var dbName = Guid.NewGuid().ToString();
if (m_databases.Add(dbName))
{
testDatabases.Add(dbName);
Assert.IsTrue(CreateDatabase(dbName), "failed to create database");
Assert.IsTrue(DeleteDatabase(dbName), "failed to delete database");
}
}
var repeatName = testDatabases.OrderBy(n => n).FirstOrDefault();
Assert.IsTrue(CreateDatabase(repeatName), "failed to create database");
Assert.IsTrue(DeleteDatabase(repeatName), "failed to delete database");

Console.WriteLine();
}

#region Helpers

private static bool CreateDatabase(string databaseName)
{
Console.Write("creating database '" + databaseName + "'...");
using (var connection = CreateConnection(CreateConnectionString(databaseName)))
{
using (var context = new SomeContext(connection, false))
{
var a = context.As.ToList(); // CompatibleWithModel must not be the first call
var result = context.Database.CompatibleWithModel(false);
Console.WriteLine(result ? "DONE" : "FAIL");
return result;
}
}
}


private static bool DeleteDatabase(string databaseName)
{
using (var connection = CreateConnection(CreateConnectionString(databaseName)))
{
if (System.Data.Entity.Database.Exists(connection))
{
Console.Write("deleting database '" + databaseName + "'...");
var result = System.Data.Entity.Database.Delete(connection);
Console.WriteLine(result ? "DONE" : "FAIL");
return result;
}
return true;
}
}

private static DbConnection CreateConnection(string connectionString)
{
return new SqlConnection(connectionString);
}

private static string CreateConnectionString(string databaseName)
{
var builder = new SqlConnectionStringBuilder
{
DataSource = "server",
InitialCatalog = databaseName,
IntegratedSecurity = false,
MultipleActiveResultSets = false,
PersistSecurityInfo = true,
UserID = "username",
Password = "password"
};
return builder.ConnectionString;
}

#endregion

}


RepeatedCreateDeleteDifferentName completes successfully, the other two fail. According to this, you cannot create a database with the same name, already used once before. When trying to create the database for the second time, the test (and application) throw a SqlException, noting a failed login. Is this a bug in Entity Framework or is this behaviour intentional (with what explanation)?


I tested this on a Ms SqlServer 2012 and Express 1014, not yet on Oracle. By the way: EF seems to have a problem with CompatibleWithModel being the first call to the database.


Aucun commentaire:

Enregistrer un commentaire