Ответ 1
Спасибо за опубликованный ответ внизу. Я копал часами для одного и того же случая и
GC.Collect ();
GC.WaitForPendingFinalizers ();
сделал трюк.
Final Edit: Я нашел решение проблемы (в нижней части вопроса).
У меня проблема Nunit, которая вызывает у меня горе. Изменить: На самом деле это больше похоже на проблему SQLite, но я еще не уверен на 100%.
В My TestFixture есть настройка, которая генерирует случайное имя файла, которое используется в качестве базы данных SQLite в каждом из моих тестов.
[Setup]
public void Setup()
{
// "filename" is a private field in my TestFixture class
filename = ...; // generate random filename
}
Каждый из моих тестов использует эту конструкцию в каждом методе, который обращается к базе данных:
[Test]
public void TestMethod()
{
using (var connection = Connect())
{
// do database activity using connection
// I've tried including this line but it doesn't help
// and is strictly unnecessary:
connection.Close();
}
}
private DbConnection Connect()
{
var connection = DbProviderFactories.GetFactory("System.Data.SQLite").CreateConnection();
connection.ConnectionString = "Data Source=" + filename;
connection.Open();
return connection;
}
Итак, для всех методов используется один вспомогательный метод Connect()
. Я предполагаю, что конструкция using() { }
вызывает Dispose()
в соединении в конце TestMethod()
и освобождает соединение с файлом базы данных SQLite.
Проблема у меня есть в моем методе [TearDown]:
[TearDown]
public void Cleanup()
{
File.Delete(filename); // throws an IOException!
}
С каждым тестом я получаю исключение:
System.IO.IOException: The process cannot access the file 'testdatabase2008-12-17_1030-04.614065.sqlite' because it is being used by another process.
Все тесты терпят неудачу, когда они добираются до [TearDown], поэтому я заканчиваю каталогом, полным временных файлов базы данных (по одному на тест, каждый с другим именем) и целым рядом неудачных тестов.
Какой процесс имеет доступ к файлу? Я не понимаю, как второй процесс может получить доступ к файлу. connection
полностью вышел из области действия и был Dispose() d к моменту, когда я пытаюсь удалить файл, поэтому он не может быть связан с SQLite. Может ли это?
Обратите внимание, что я получаю тот же результат, если я запускаю все тесты или только один тест.
Обновление: Итак, я попытался удалить или удалить мои объекты DbCommand, так как я этого не делал (я предположил, что каждый другой поставщик ADO.NET, который Dispose() использует DbConnection также Dispose() s любые команды в этом соединении.) Теперь они выглядят так:
[Test]
public void TestMethod()
{
using (var connection = Connect())
{
using (var command = connection.CreateCommand())
{
// do database activity using connection
}
}
}
Это не имело никакого значения - строка File.Delete() по-прежнему вызывает исключение IOException.: - (
Если я удалю эту строку в [TearDown], все мои тесты пройдут, но у меня останется целая куча временных файлов базы данных.
Другое обновление: Это прекрасно работает:
var filename = "testfile.sqlite";
using (var connection = BbProviderFactories.GetFactory("System.Data.SQLite").CreateConnection())
{
connection.ConnectionString = "Data Source=" + filename;
connection.Open();
var createCommand = connection.CreateCommand();
createCommand.CommandText =
"CREATE TABLE foo (id integer not null primary key autoincrement, bar text not null);";
createCommand.ExecuteNonQuery();
var insertCommand = connection.CreateCommand();
insertCommand.CommandText = "INSERT INTO foo (bar) VALUES (@bar)";
insertCommand.Parameters.Add(insertCommand.CreateParameter());
insertCommand.Parameters[0].ParameterName = "@bar";
insertCommand.Parameters[0].Value = "quux";
insertCommand.ExecuteNonQuery();
}
File.Delete(filename);
Я не понимаю!
Обновление: найденное решение:
[TearDown]
public void Cleanup()
{
GC.Collect();
File.Delete(filename);
}
Я запускал модульные тесты через отладчик, и когда запускается метод [TearDown]
, определенно нет ссылок на SQLite DbConnection. Принуждение GC должно их очистить. В SQLite должна быть ошибка.
Спасибо за опубликованный ответ внизу. Я копал часами для одного и того же случая и
GC.Collect ();
GC.WaitForPendingFinalizers ();
сделал трюк.
попробуйте вызвать Close on dbconnection
убедитесь, что процесс sqllite завершен
вы можете видеть, какой процесс заблокирован вашим файлом с помощью утилиты Unlocker (бесплатно)
это может быть "известная" проблема с SqlLite; форум подшучивает над закрытием соединения и распоряжением командой и предполагает, что это будет исправлено в будущей версии (поскольку это поведение не согласуется с другими поставщиками ADO)
Сброс выполняется после каждого теста.
Этот атрибут используется внутри TestFixture для обеспечения общего набора функций, которые выполняются после запуска каждого тестового метода.
Вам следует попытаться удалить их все с помощью TestFixtureTearDown:
[TestFixtureTearDown]
public void finish()
{
//Delete all file
}
Возможно, один тест использует файл, который вы пытаетесь удалить в другом тесте. < - [Стюарт] Как я сказал в вопросе, это происходит, когда я запускаю только один тест, поэтому это невозможно.
Update Вы не даете достаточно информации о том, что делаете. Вы пытаетесь очистить весь тестовый файл только одним тестом в тестовом файле и попробовать? [Стюарт] Да. Если он работает [Стюарт] (это не так), то это значит, что у вас есть несколько тестовых проблем (они обращаются друг к другу). Чтобы найти источник, вам нужно сократить проблему. Затем вернитесь, мы поможем вам. на данный момент он только догадывается, что мы можем дать вам. [Стюарт] Я уже сделал эти сокращения проблемы, которые вы предлагаете, вы обнаружите, что они в моем первоначальном вопросе!
Мне нужно будет увидеть логику создания вашего файла, но возможно, что вы открываете файл для его создания, но не закрываете его после его создания. Я думаю, что если вы используете System.IO.Path.GetTempFileName(), он просто создаст файл и вернет вам имя файла с закрытым файлом. Если вы делаете свое собственное генерирование случайных имен и используете File.Open, вам нужно будет убедиться, что он закрыт впоследствии.
С другой стороны, я сердечно рекомендую придерживаться насмешливой стратегии абстрагирования базы данных, а не чтения/записи из реальной базы данных в своих модульных тестах. Точка модульных тестов заключается в том, что они должны быть очень быстрыми, и файлы ввода/вывода будут действительно замедлять их.
У вас работает Антивирус? Некоторые AV-продукты просматривают файл, когда видят, что они закрыты. Если у программного обеспечения AV по-прежнему открыт файл, когда вы его удаляете, вы увидите эту проблему. Вы можете повторить удаление после небольшой задержки.
Я также видел, как это происходит с поисковыми индексаторами.
Первый шаг - найти, кто держит дескриптор соответствующего файла.
Получить копию Process Explorer Запустить. Нажмите Ctrl + F и введите имя файла. Он покажет вам список процессов, обращающихся к нему.
Возможно ли, что файл находится в процессе закрытия (I.e. SQLLite не выпускает его немедленно)?
Вы можете попробовать положить db close в цикл задержки (возможно, одну секунду между каждой попыткой) и только бросать исключение после нескольких (5 или около того) итераций через цикл.
Что вы используете для открытия своей базы данных? Вы используете разъем ADO 2.0 от здесь. Если у вас есть приложение, которое его использует, и я могу сделать с ним несколько соединений (закрыть/открыть). Если вы не используете этот разъем, вы можете попробовать. Что возвращает ваш метод Connect()?
Спасибо, что указали это!
Проблема не связана с SQLite, а скорее с управлением памятью в целом. После запуска теста объекты, указывающие на файлы, больше не имеют области видимости, но они все еще существуют в памяти.
Следовательно, все еще есть ссылки на файлы, и вы не можете их удалить или переместить.
Я знаю, что этот ответ уже более года, но для тех, кто читает в будущем...
У меня была аналогичная проблема для вас - попытка удалить тестовые базы данных между испытаниями не удалась из-за того, что файл базы данных SQLite остался открытым. Я проследил проблему в моем коде, чтобы объект SQLiteDataReader не был явно закрыт.
SQLiteDataReader dr = cmd_.ExecuteReader();
while (dr.Read())
{
// use the DataReader results
}
dr.Close(); // <-- necessary for database resources to be released
Вызов статического метода
SqliteConnection.ClearAllPools()
После этого вызова файл базы данных разблокируется, и вы можете удалить файл в [TearDown].