Finalement, après enquête et un peu de jeu autour, j'ai réalisé cela en procédant comme suit:
Création d'un Entity Framework Initializer basé sur CreateDatabaseIfNotExists (http://www.entityframeworktutorial.net/code-first/database-initialization-strategy-in-code-first.aspx):
public class DbTestInitializer : CreateDatabaseIfNotExists<DBContext>
{
public override void InitializeDatabase(DBContext context)
{
if(context.Database.Exists())
{
// We have a database already, so we can clean entities and run seed.
CleanseTables(context);
Seed(context);
}
base.InitializeDatabase(context);
}
private void CleanseTables(DBContext context)
{
// Run the database teardown script
context.Database.ExecuteSqlCommand(Properties.Resources.DatabaseTeardown);
}
protected override void Seed(DBContext context)
{
this.ApplySeed(context);
}
}
A l'intérieur du initialiseur InitializeDatabase méthode, je vérifie pour voir si la base de données existe - si oui, alors j'exécute un script de nettoyage qui fait ce qui suit:
- Collecté toutes les références de clés étrangères et construit une contrainte de dépôt et créer un script de contrainte pour chacun. J'ai ensuite exécuté les contraintes de suppression, tronqué toutes les tables de la base de données (à l'exclusion de la table MigrationHistory spécifique aux migrations EF) et réactivé les contraintes à l'aide des scripts create constraint. Cela se fait en exécutant le script via context.Database.ExecuteSQLCommand ([scénario])
Voilà comment ce script ressemble.
DECLARE @ConstraintsTable TABLE
(
ID INT IDENTITY(1,1),
DropConstraintScript VARCHAR(MAX),
EnableConstraintScript VARCHAR(MAX)
)
INSERT INTO @ConstraintsTable
SELECT
'ALTER TABLE [' + ForeignKeys.ForeignTableSchema
+ '].[' + ForeignKeys.ForeignTableName + '] DROP CONSTRAINT ['
+ ForeignKeys.ForeignKeyName + ']; ',
'ALTER TABLE [' + ForeignKeys.ForeignTableSchema
+ '].[' + ForeignKeys.ForeignTableName
+ '] WITH CHECK ADD CONSTRAINT [' + ForeignKeys.ForeignKeyName
+ '] FOREIGN KEY([' + ForeignKeys.ForeignTableColumn
+ ']) REFERENCES [' + SCHEMA_NAME(sys.objects.schema_id)
+ '].[' + sys.objects.[name] + ']([' + sys.columns.[name]
+ ']);'
FROM sys.objects
INNER JOIN sys.columns
ON (sys.columns.[object_id] = sys.objects.[object_id])
INNER JOIN (SELECT sys.foreign_keys.[name] AS ForeignKeyName
,SCHEMA_NAME(sys.objects.schema_id) AS ForeignTableSchema
,sys.objects.[name] AS ForeignTableName
,sys.columns.[name] AS ForeignTableColumn
,sys.foreign_keys.referenced_object_id AS referenced_object_id
,sys.foreign_key_columns.referenced_column_id AS referenced_column_id
FROM sys.foreign_keys
INNER JOIN sys.foreign_key_columns
ON (sys.foreign_key_columns.constraint_object_id = sys.foreign_keys.[object_id])
INNER JOIN sys.objects
ON (sys.objects.[object_id] = sys.foreign_keys.parent_object_id)
INNER JOIN sys.columns
ON (sys.columns.[object_id] = sys.objects.[object_id])
AND (sys.columns.column_id = sys.foreign_key_columns.parent_column_id)
) ForeignKeys
ON (ForeignKeys.referenced_object_id = sys.objects.[object_id])
AND (ForeignKeys.referenced_column_id = sys.columns.column_id)
WHERE (sys.objects.[type] = 'U')
AND (sys.objects.[name] NOT IN ('sysdiagrams'))
declare @count int, @ndx int
declare @script nvarchar(max)
select @count = count(ID) from @ConstraintsTable
set @ndx = 1
while(@ndx <= @count)
begin
select @script = DropConstraintScript from @ConstraintsTable where ID = @ndx
EXEC sp_executesql @script;
set @ndx = @ndx + 1
end
EXEC sp_msforeachtable @command1 = 'TRUNCATE TABLE ?', @whereand = 'AND Object_Id NOT IN (SELECT Object_Id FROM sys.objects WHERE name like ''__Migration%'')';
set @ndx = 1
while(@ndx <= @count)
begin
select @script = EnableConstraintScript from @ConstraintsTable where ID = @ndx
EXEC sp_executesql @script;
set @ndx = @ndx + 1
end
Le script
EXEC sp_msforeachtable @command1 = 'TRUNCATE TABLE ?', @whereand = 'AND Object_Id NOT IN (SELECT Object_Id FROM sys.objects WHERE name like ''__Migration%'')';
tronque toutes les tables de la base de données, sauf pour la table de migration. C'est après les scripts de contrainte de dépôt ont été exécutés. Après que les tables sont tronquées, la partie suivante du script ajoute toutes les contraintes. En utilisant des variables de table au lieu de curseurs, nous devrions obtenir de meilleures performances lorsque le script s'exécute. Il peut être possible d'améliorer le script dans des endroits pour obtenir de meilleures performances. C'est la seule zone où nous comptons sur l'exécution de scripts de base de données. En utilisant les avantages de l'EF Initializer, nous assurons ce qui suit:
- La base de données peut être créée à partir de zéro si elle n'est pas déjà créée.
- La base de données est nettoyée (toutes les tables sont tronquées) en utilisant le script détaillé ci-dessus dans la méthode CleanseTables.
- La base de données est ensuite ensemencée. Dans mon exemple, j'ai utilisé le même amorçage pour l'initialiseur db de test que j'ai pour mon initialiseur de migration par défaut. Cela garantit que la base de données est «réinitialisée» à l'état où se trouvait la base de données avant l'ajout de données.
Oui, j'utilise déjà cette approche et il existe déjà des cas où je gère des données a été géré dans les tests. Cependant, à la suite des tests automatisés sur le Web, il y aura également des données qui sont insérées/modifiées au cours d'un test qui ne peut pas être contrôlé. Ce que je cherche, c'est une solution pour 'réinitialiser' la base de données à l'état dans lequel elle était avant l'exécution du test. –
Pourquoi ne pas tronquer les tables et y insérer les données initiales? Cela fait partie de la gestion des données de test. Vous devez avoir le contrôle sur les données de test. Stockez-le dans un fichier script sql et exécutez-le à chaque test ou lorsque cela est nécessaire. – SayusiAndo
Je l'ai implémenté en procédant comme suit: - Ajout d'un initialiseur EF spécifique à ma suite de tests uniquement. Il est basé sur l'initialiseur CreateDatabaseIfNotExists, donc si la base de données n'existe pas, elle sera créée.Si elle existe, alors j'ai activé de sorte que toutes les entités sont récupérées et tronquées, puis l'ensemencement s'exécute à nouveau. J'ai joué avec le laisser juste pour supprimer et créer la base de données, qui fonctionne très bien. Le problème est que lorsque les tests commencent à se développer et que les données deviennent plus volumineuses, les performances de l'initialiseur lors de la suppression et de la recréation de la base de données sont extrêmement médiocres. –