EF 初始化结束所有的Connection

本文介绍了一种解决Entity Framework在单元测试中因数据库连接未关闭而导致无法删除数据库的问题的方法。通过在自定义数据库初始化器中加入关闭所有连接的代码,确保了数据库能够被正确初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Here’s an Entity Framework scenario for you:

You’re using Entity Framework 6.

Check. You’re using a custom database initializer to init your DEV and TEST databases. Specifically the DropCreateDatabaseAlways initializer.

Yup, that's me. Your project contains 1 or more Unit Test projects that utilize the above.

Do you have a unit test project? Say yes. You have a connection open to the database that the initializer is going to drop.

Say, via SSMS. You run your unit tests. And it runs, and runs, and runs … and then : 51485cb2b3ab8.preview-620

System.Data.SqlClient.SqlException: Cannot drop database "Foo" because it is currently in use.. Aborting test execution. Result StackTrace: at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.b__0(DbCommand t, DbCommandInterceptionContext1 c) at

blahdy blah blah blah ... Ok, so what’s happening? The initializer cannot drop the database because there is currently an open connection. The most simple way of getting rid of open connections is either to delete the DB in your SSMS (with the close all connections checkbox checked), or run some SQL in a query window that accomplishes the same task.

How annoyed are you after the 10th time of doing this? The correct answer is: very.

Give Those Connections a Dirt Nap

There is a fairly simple way to combat this problem by embedding the code necessary to kill open connections directly into the database initializer. Here’s what you do:

In your custom initializer that inherits from DropCreateDatabaseAlways, add the following method (you can rename it to something a little less Class A felony if you wish):

private void MurderAllConnections(HumanResourceContext context)
{
 
try
{
 
// FIRST: Build a connection using the DB Context's current connection.
SqlConnectionStringBuilder sqlConnBuilder = new SqlConnectionStringBuilder(context.Database.Connection.ConnectionString);
 
// Set the catalog to master so that the DB can be dropped
sqlConnBuilder.InitialCatalog = "master";
using (SqlConnection sqlConnection = new SqlConnection(sqlConnBuilder.ConnectionString))
{
 
   sqlConnection.Open();
   string dbName = context.Database.Connection.Database;
 
   // Build up the SQL string necessary for dropping database connections. This statement is doing a couple of things:
   // 1) Tests to see if the DB exists in the first place.
   // 2) If it does, sets single user mode, which kills all connections.
   string sql = @"IF EXISTS(SELECT NULL FROM sys.databases WHERE name = '" + dbName + "') BEGIN ALTER DATABASE [" + dbName + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE END";
 
   using (SqlCommand sqlCmd = new SqlCommand(sql, sqlConnection))
   {
       // Run and done.
       sqlCmd.CommandType = System.Data.CommandType.Text;
       sqlCmd.ExecuteNonQuery();
   }
}
}
catch
{
   // Something bad happened.
   throw new Exception("Hey, boss, the UnitTestInitializer failed. You want I should fix it?");
}
 
}

Now with the above method in place, the add it to the InitializeDatabase() method in your custom initializer:

public override void InitializeDatabase(FooContext context)
{
   this.MurderAllConnections(context);
   base.InitializeDatabase(context);
}

Now all those connections are sleepin’ with the fishes.

Some (hopefully obvious) Caveats

Make sure the MurderAllConnections() call runs before the base. Do not name your method MurderAllConnections(). That’s just bad form. Your connection will need to be in windows auth mode. Highly recommend NOT using this in a PROD or STAGING environment. Just sayin’. Happy Entity Frameworking …

Cheers, Jim

转载于:https://my.oschina.net/ITELITE/blog/601977

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值