В конце концов, после расследования и немного играть вокруг, я достиг этого, выполнив следующие действия:
создал Entity Framework Initializer на основе 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);
}
}
Внутри инициализатора InitializeDatabase, я проверяю, существует ли база данных - если это так, я выполняю сценарий очистки, который выполняет следующие действия:
- Собрано все ссылки на внешние ключи и построено ограничение на отказ и создать ограничение скрипт для каждого из них. Затем я выполнил ограничения на падение, усеченные каждую таблицу в базе данных (за исключением таблицы MigrationHistory, специфичной для EF Migrations), и повторно связали ограничения с помощью сценариев create. Это делается путем выполнения сценария с помощью context.Database.ExecuteSQLCommand ([сценарий])
Вот как этот сценарий выглядит.
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
Сценарий
EXEC sp_msforeachtable @command1 = 'TRUNCATE TABLE ?', @whereand = 'AND Object_Id NOT IN (SELECT Object_Id FROM sys.objects WHERE name like ''__Migration%'')';
Усекает все таблицы в базе данных для таблицы миграции, за исключением. Это после выполняются сценарии ограничения на ограничение. После того, как таблицы усечены, следующая часть скрипта добавляет все ограничения обратно. Используя переменные таблицы вместо курсоров, мы должны получить лучшую производительность при выполнении скрипта. Возможно, можно улучшить скрипт в местах, чтобы получить лучшую производительность. Это единственная область, где мы полагаемся на выполнение сценария базы данных. Путем использования преимуществ EF Initializer мы гарантируем следующее:
- База данных может быть создана с нуля, если она еще не создана.
- База данных очищается (все таблицы усечены), используя сценарий, описанный выше в методе CleanseTables.
- База данных затем высевается. В моем экземпляре я использовал тот же самый посев для инициализатора тестового db, который у меня есть для инициализатора миграции по умолчанию. Это гарантирует, что база данных будет «сброшена» до состояния, в котором база данных была до того, как были добавлены какие-либо данные.
Да, я уже использую этот подход, и уже есть случаи, когда я управляю данными, управляется в тестах. Однако в результате автоматизированного веб-тестирования также будут данные, которые вставляются/корректируются во время теста, который нельзя контролировать. То, что я ищу, - это решение «сбросить» базу данных до состояния, которое было до запуска теста. –
Почему бы не обрезать таблицы и вставить в нее исходные данные? Это часть управления тестовыми данными. Вы должны контролировать контрольные данные. Храните его в файле сценария sql и запускайте его при каждом тестировании или когда это необходимо. – SayusiAndo
Я реализовал это, выполнив следующее: добавлен EF-инициализатор, специфичный только для моего набора тестов. Он основан на инициализаторе CreateDatabaseIfNotExists, поэтому, если база данных не существует, она будет создана.Если он существует, то я включил, чтобы все объекты были извлечены и усечены, а затем сеяние снова запускается. Я играл, оставив его просто удалить и создать базу данных, которая отлично работает. Проблема в том, что по мере того, как тесты начинают расти, а данные становятся более экспансивными, производительность инициализатора при отбрасывании и воссоздании базы данных будет крайне низкой. –