初始化数据库时出现 SqlException,但仅限于 Azure 且仅限于 CF 迁移
SqlException while initializing database, but only on Azure and only with CF Migrations
当针对 Azure SQL 服务器数据库 运行 初始化时,我得到一个 SqlException
:The server was not found or was not accessible
.
这是我的 Context
相关部分的代码:
Private Sub New(Connection As DbConnection)
MyBase.New(Connection, True)
Database.SetInitializer(New CreateDatabaseIfNotExists(Of Context))
Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of Context, Migrations.Configuration))
Try
Me.Database.Initialize(False)
Catch ex As Exception
HttpContext.Current.Response.Write(ex.ToString)
HttpContext.Current.Response.End()
End Try
End Sub
Public Shared Function Create() As Context
Return New Context(DbConnection)
End Function
这是完整的异常堆栈跟踪:
1. System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): Access is denied
2. at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, DbConnectionPool pool, String accessToken, Boolean applyTransientFaultHandling, SqlAuthenticationProviderManager sqlAuthProviderManager)
3. at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
4. at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
5. at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
6. at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
7. at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
8. at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
9. at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
10. at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
11. at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
12. at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
13. at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
14. at System.Data.SqlClient.SqlConnection.Open()
15. at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.<>c.<Open>b__13_0(DbConnection t, DbConnectionInterceptionContext c)
16. at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext](TTarget target, Action`2 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
17. at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.Open(DbConnection connection, DbInterceptionContext interceptionContext)
18. at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass60_0.<UsingConnection>b__0()
19. at System.Data.Entity.Infrastructure.DbExecutionStrategy.<>c__DisplayClass17_0.<Execute>b__0()
20. at System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute[TResult](Func`1 operation)
21. at System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute(Action operation)
22. at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action`1 act)
23. at System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection, Action`1 act)
24. at System.Data.Entity.SqlServer.SqlProviderServices.CreateDatabaseFromScript(Nullable`1 commandTimeout, DbConnection sqlConnection, String createDatabaseScript)
25. at System.Data.Entity.SqlServer.SqlProviderServices.DbCreateDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
26. at System.Data.Entity.Core.Common.DbProviderServices.CreateDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
27. at System.Data.Entity.Core.Objects.ObjectContext.CreateDatabase()
28. at System.Data.Entity.Migrations.Utilities.DatabaseCreator.Create(DbConnection connection)
29. at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
30. at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
31. at System.Data.Entity.MigrateDatabaseToLatestVersion`2.InitializeDatabase(TContext context)
32. at System.Data.Entity.Internal.InternalContext.<>c__DisplayClass66_0`1.<CreateInitializationAction>b__0()
33. at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
34. at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
35. at System.Data.Entity.Internal.LazyInternalContext.<>c.<InitializeDatabase>b__58_0(InternalContext c)
36. at System.Data.Entity.Internal.RetryAction`1.PerformAction(TInput input)
37. at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action`1 action)
38. at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
39. at System.Data.Entity.Internal.InternalContext.Initialize()
40. at Website.Db.Context..ctor(DbConnection Connection) in D:\Dev\Application\__Legacy\Website\Db\Context.vb:line 24
41. ClientConnectionId:00000000-0000-0000-0000-000000000000
42. Error Number:5,State:0,Class:20
现在,当我在初始化尝试之前插入一些测试代码时,例如:
Public Shared Function Create() As Context
Return New Context(DbConnection)
End Function
Private Sub New(Connection As DbConnection)
MyBase.New(Connection, True)
Database.SetInitializer(New CreateDatabaseIfNotExists(Of Context))
Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of Context, Migrations.Configuration))
Dim sConnectionString As String = Connection.ConnectionString
HttpContext.Current.Response.Write($"Connection string: {sConnectionString}")
HttpContext.Current.Response.Write("<br />")
HttpContext.Current.Response.Write($"Can connect: {Db.Connection.CanConnect(sConnectionString)}")
HttpContext.Current.Response.End()
Try
Me.Database.Initialize(False)
Catch ex As Exception
HttpContext.Current.Response.Write(ex.ToString)
HttpContext.Current.Response.End()
End Try
End Sub
...我的字符串是正确的,连接顺利通过。只有当我尝试通过我的上下文连接时它才会失败(即 Me.Database.Initialize(False)
)。
这是我的 CanConnect
代码:
Public Shared Function CanConnect(ConnectionString As String) As Boolean
CanConnect = True
Try
Utils.ExecuteNonQuery("SELECT * FROM INFORMATION_SCHEMA.TABLES", ConnectionString)
Catch ex As Exception
CanConnect = False
End Try
End Function
Public Shared Function ExecuteNonQuery(CommandText As String, ConnectionString As String) As Integer
Using oSqlCnn As New SqlConnection(ConnectionString)
Using oSqlCmd As SqlCommand = oSqlCnn.CreateCommand
oSqlCmd.CommandType = CommandType.Text
oSqlCmd.CommandText = CommandText
oSqlCnn.Open()
Return oSqlCmd.ExecuteNonQuery
End Using
End Using
End Function
因此,普通 ADO.NET 连接可以通过,而 EF Code First 连接则不能——两者都使用相同的连接字符串。很奇怪。
更神秘的是,所有这些在我连接到本地安装的 SQLEXPRESS 实例的开发机器上运行良好。我的上下文在此处连接和应用迁移没有问题。它只在 Azure 上失败。
搜索没有显示任何内容,大部分只是关于如何正确配置迁移方案的建议。正如我的开发机器所证明的那样,我已经这样做了。
我简要地查看了 SqlException
的成员,我想我可能会得到连接字符串并检查它的准确性,但这不是一个选择。
为什么此连接尝试仅在上下文中且仅在 Azure 上失败?我怎样才能找到它以便修复它?
--编辑--
这是我的连接字符串(为安全起见进行了清理):
Server=tcp:some.database.windows.net,1433;Initial Catalog=somedb;Persist Security Info=False;User ID=username;Password=password;
--编辑--
有些进展停滞不前,但新信息使情况变得更加模糊。
根据 this answer,我正在等待 Azure SQL 工程师的确认,System.Data.SqlClient
首先尝试 TCP 连接(假设它被指示通过连接字符串中的 tcp:
前缀)。如果第一次尝试失败,客户端将回退到命名管道。
这似乎就是这种情况下发生的情况,因为堆栈跟踪指示命名管道失败(@AlwaysLearning 在评论中注意到这一点很有帮助)。当然,Azure SQL 不支持命名管道,因此此时会出现连接失败。
所以我一开始以为我们离解决方案又近了一步,直到我遇到这个:对服务器 sys.event_log
的查询显示几乎一致的成功连接记录。
这真的很奇怪。
--编辑--
在我的本地开发机器上禁用命名管道的测试没有显示任何内容。仅在 TCP 上连接就顺利通过。
所以缩小了一点。失败仅通过 EF6 发生,并且仅发生在 Azure 上。 (但连接日志显示成功。)
这真让人头疼。
--编辑--
我已经能够确认日志中的成功连接来自我测试的 ADO.NET 个连接(以上)。
失败的连接不会写入日志,因为无法找到服务器来记录它们(至少是命名管道尝试)。这仍然是个大谜。
--编辑--
我在 EntityFramework repo 上开了一个问题并发布了一个 repro 项目:
--编辑--
线索不断涌现,但它们继续指向各个不同的方向。
我在同一台服务器上有另一个数据库,其网站使用与我在这里使用的完全相同的迁移代码。该应用程序工作正常,连接字符串几乎相同。
令人困惑。但是这个问题是服务器层面的,不是数据库层面的。
是否可以在连接字符串到达数据库之前拦截它,以验证它在堆栈中没有被破坏?
问题已解决
精明的开发人员(不是我)会在重现项目的提交 3c20e41
的 Context
class 中注意到我在两个不同的地方构建了两个单独的连接字符串。当时,我误以为 CF Migrations 只对 design-time 操作使用默认构造函数,例如Add-Migration
、Update-Database
等
事实证明,这显然是不是的情况。 DbMigrator 运行时代码执行确实会循环回调用默认构造函数(有一个隐藏的线索:请参阅上面堆栈跟踪中的第 31 行)。默认构造函数是我获取 design-time 连接字符串的地方。这就是 Azure-based 连接尝试失败的原因——因为它找不到 design-time 字符串提供的服务器名称。顺其自然。
现在检查提交 d45888b
中修复的代码。只有一个连接字符串源,如果 Configuration.ConnectionStrings
为 Nothing
,则该源仅构建 design-time 字符串。 (我以前遇到过这种情况,这就是我构建 two-string 概念的原因——我只是没有深入了解它。)
这就是它在我的本地开发机器上运行良好的原因——因为我使用的是 design-time 字符串。
我只是偶然发现了这一切。我正在制定另一个测试方案,我在 design-time string builder 中混淆了用户名和密码值。连接失败报告表明这次登录失败,显示尝试的用户名。等一下!那是我的密码!呸
唯一可以生成 design-time 字符串的方法(在那个版本的代码下)是在调用默认构造函数的过程中。根据我之前的理解,这不是在运行时发生的。
生活和学习。
这是直接的代码,以防在遥远的将来的某个时候我决定取消回购协议。可能,但不太可能。我宁愿把它作为有问题的编程实践的纪念品。
旧上下文(损坏)
Imports System.Data.Common
Imports System.Data.Entity
Imports System.Data.SqlClient
Imports System.Reflection
Imports DbConnectionTest.Db.Models
Namespace Db
Public Class Context
Inherits DbContext
Public Sub New()
MyBase.New(Utils.DesignTimeConnectionString) ' <--
End Sub
Private Sub New(Connection As DbConnection)
MyBase.New(Connection, True)
Database.SetInitializer(New CreateDatabaseIfNotExists(Of Context))
Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of Context, Migrations.Configuration))
Me.Database.Initialize(False)
End Sub
Public Shared Function Create() As Context
Return New Context(New SqlConnection(Utils.RunTimeConnectionString)) ' <--
End Function
Protected Overrides Sub OnModelCreating(Builder As DbModelBuilder)
Builder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly)
MyBase.OnModelCreating(Builder)
End Sub
Protected Overrides Sub Dispose(Disposing As Boolean)
MyBase.Dispose(Disposing)
End Sub
Public Overridable Property Customers As DbSet(Of Customer)
Public Overridable Property Invoices As DbSet(Of Invoice)
End Class
End Namespace
新实用程序(正在运行)
Imports System
Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient
Namespace Db
Friend Class Utils
Friend Shared ReadOnly Property DbConnectionString() As String
Get
If ConfigurationManager.ConnectionStrings Is Nothing Then
With New SqlConnectionStringBuilder
.MultipleActiveResultSets = True
.PersistSecurityInfo = False
.IntegratedSecurity = False
.InitialCatalog = DB_NAME
.DataSource = Environment.MachineName
.Password = ""
.UserID = ""
DbConnectionString = .ConnectionString
End With
Else
DbConnectionString = ConfigurationManager.ConnectionStrings(DB_NAME).ConnectionString
End If
End Get
End Property
Friend Const DB_NAME As String = "DbConnectionTest"
End Class
End Namespace
当针对 Azure SQL 服务器数据库 运行 初始化时,我得到一个 SqlException
:The server was not found or was not accessible
.
这是我的 Context
相关部分的代码:
Private Sub New(Connection As DbConnection)
MyBase.New(Connection, True)
Database.SetInitializer(New CreateDatabaseIfNotExists(Of Context))
Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of Context, Migrations.Configuration))
Try
Me.Database.Initialize(False)
Catch ex As Exception
HttpContext.Current.Response.Write(ex.ToString)
HttpContext.Current.Response.End()
End Try
End Sub
Public Shared Function Create() As Context
Return New Context(DbConnection)
End Function
这是完整的异常堆栈跟踪:
1. System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): Access is denied
2. at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, DbConnectionPool pool, String accessToken, Boolean applyTransientFaultHandling, SqlAuthenticationProviderManager sqlAuthProviderManager)
3. at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
4. at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
5. at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
6. at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
7. at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
8. at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
9. at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
10. at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
11. at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
12. at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
13. at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
14. at System.Data.SqlClient.SqlConnection.Open()
15. at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.<>c.<Open>b__13_0(DbConnection t, DbConnectionInterceptionContext c)
16. at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext](TTarget target, Action`2 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
17. at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.Open(DbConnection connection, DbInterceptionContext interceptionContext)
18. at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass60_0.<UsingConnection>b__0()
19. at System.Data.Entity.Infrastructure.DbExecutionStrategy.<>c__DisplayClass17_0.<Execute>b__0()
20. at System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute[TResult](Func`1 operation)
21. at System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute(Action operation)
22. at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action`1 act)
23. at System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection, Action`1 act)
24. at System.Data.Entity.SqlServer.SqlProviderServices.CreateDatabaseFromScript(Nullable`1 commandTimeout, DbConnection sqlConnection, String createDatabaseScript)
25. at System.Data.Entity.SqlServer.SqlProviderServices.DbCreateDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
26. at System.Data.Entity.Core.Common.DbProviderServices.CreateDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
27. at System.Data.Entity.Core.Objects.ObjectContext.CreateDatabase()
28. at System.Data.Entity.Migrations.Utilities.DatabaseCreator.Create(DbConnection connection)
29. at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
30. at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
31. at System.Data.Entity.MigrateDatabaseToLatestVersion`2.InitializeDatabase(TContext context)
32. at System.Data.Entity.Internal.InternalContext.<>c__DisplayClass66_0`1.<CreateInitializationAction>b__0()
33. at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
34. at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
35. at System.Data.Entity.Internal.LazyInternalContext.<>c.<InitializeDatabase>b__58_0(InternalContext c)
36. at System.Data.Entity.Internal.RetryAction`1.PerformAction(TInput input)
37. at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action`1 action)
38. at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
39. at System.Data.Entity.Internal.InternalContext.Initialize()
40. at Website.Db.Context..ctor(DbConnection Connection) in D:\Dev\Application\__Legacy\Website\Db\Context.vb:line 24
41. ClientConnectionId:00000000-0000-0000-0000-000000000000
42. Error Number:5,State:0,Class:20
现在,当我在初始化尝试之前插入一些测试代码时,例如:
Public Shared Function Create() As Context
Return New Context(DbConnection)
End Function
Private Sub New(Connection As DbConnection)
MyBase.New(Connection, True)
Database.SetInitializer(New CreateDatabaseIfNotExists(Of Context))
Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of Context, Migrations.Configuration))
Dim sConnectionString As String = Connection.ConnectionString
HttpContext.Current.Response.Write($"Connection string: {sConnectionString}")
HttpContext.Current.Response.Write("<br />")
HttpContext.Current.Response.Write($"Can connect: {Db.Connection.CanConnect(sConnectionString)}")
HttpContext.Current.Response.End()
Try
Me.Database.Initialize(False)
Catch ex As Exception
HttpContext.Current.Response.Write(ex.ToString)
HttpContext.Current.Response.End()
End Try
End Sub
...我的字符串是正确的,连接顺利通过。只有当我尝试通过我的上下文连接时它才会失败(即 Me.Database.Initialize(False)
)。
这是我的 CanConnect
代码:
Public Shared Function CanConnect(ConnectionString As String) As Boolean
CanConnect = True
Try
Utils.ExecuteNonQuery("SELECT * FROM INFORMATION_SCHEMA.TABLES", ConnectionString)
Catch ex As Exception
CanConnect = False
End Try
End Function
Public Shared Function ExecuteNonQuery(CommandText As String, ConnectionString As String) As Integer
Using oSqlCnn As New SqlConnection(ConnectionString)
Using oSqlCmd As SqlCommand = oSqlCnn.CreateCommand
oSqlCmd.CommandType = CommandType.Text
oSqlCmd.CommandText = CommandText
oSqlCnn.Open()
Return oSqlCmd.ExecuteNonQuery
End Using
End Using
End Function
因此,普通 ADO.NET 连接可以通过,而 EF Code First 连接则不能——两者都使用相同的连接字符串。很奇怪。
更神秘的是,所有这些在我连接到本地安装的 SQLEXPRESS 实例的开发机器上运行良好。我的上下文在此处连接和应用迁移没有问题。它只在 Azure 上失败。
搜索没有显示任何内容,大部分只是关于如何正确配置迁移方案的建议。正如我的开发机器所证明的那样,我已经这样做了。
我简要地查看了 SqlException
的成员,我想我可能会得到连接字符串并检查它的准确性,但这不是一个选择。
为什么此连接尝试仅在上下文中且仅在 Azure 上失败?我怎样才能找到它以便修复它?
--编辑--
这是我的连接字符串(为安全起见进行了清理):
Server=tcp:some.database.windows.net,1433;Initial Catalog=somedb;Persist Security Info=False;User ID=username;Password=password;
--编辑--
有些进展停滞不前,但新信息使情况变得更加模糊。
根据 this answer,我正在等待 Azure SQL 工程师的确认,System.Data.SqlClient
首先尝试 TCP 连接(假设它被指示通过连接字符串中的 tcp:
前缀)。如果第一次尝试失败,客户端将回退到命名管道。
这似乎就是这种情况下发生的情况,因为堆栈跟踪指示命名管道失败(@AlwaysLearning 在评论中注意到这一点很有帮助)。当然,Azure SQL 不支持命名管道,因此此时会出现连接失败。
所以我一开始以为我们离解决方案又近了一步,直到我遇到这个:对服务器 sys.event_log
的查询显示几乎一致的成功连接记录。
这真的很奇怪。
--编辑--
在我的本地开发机器上禁用命名管道的测试没有显示任何内容。仅在 TCP 上连接就顺利通过。
所以缩小了一点。失败仅通过 EF6 发生,并且仅发生在 Azure 上。 (但连接日志显示成功。)
这真让人头疼。
--编辑--
我已经能够确认日志中的成功连接来自我测试的 ADO.NET 个连接(以上)。
失败的连接不会写入日志,因为无法找到服务器来记录它们(至少是命名管道尝试)。这仍然是个大谜。
--编辑--
我在 EntityFramework repo 上开了一个问题并发布了一个 repro 项目:
--编辑--
线索不断涌现,但它们继续指向各个不同的方向。
我在同一台服务器上有另一个数据库,其网站使用与我在这里使用的完全相同的迁移代码。该应用程序工作正常,连接字符串几乎相同。
令人困惑。但是这个问题是服务器层面的,不是数据库层面的。
是否可以在连接字符串到达数据库之前拦截它,以验证它在堆栈中没有被破坏?
问题已解决
精明的开发人员(不是我)会在重现项目的提交 3c20e41
的 Context
class 中注意到我在两个不同的地方构建了两个单独的连接字符串。当时,我误以为 CF Migrations 只对 design-time 操作使用默认构造函数,例如Add-Migration
、Update-Database
等
事实证明,这显然是不是的情况。 DbMigrator 运行时代码执行确实会循环回调用默认构造函数(有一个隐藏的线索:请参阅上面堆栈跟踪中的第 31 行)。默认构造函数是我获取 design-time 连接字符串的地方。这就是 Azure-based 连接尝试失败的原因——因为它找不到 design-time 字符串提供的服务器名称。顺其自然。
现在检查提交 d45888b
中修复的代码。只有一个连接字符串源,如果 Configuration.ConnectionStrings
为 Nothing
,则该源仅构建 design-time 字符串。 (我以前遇到过这种情况,这就是我构建 two-string 概念的原因——我只是没有深入了解它。)
这就是它在我的本地开发机器上运行良好的原因——因为我使用的是 design-time 字符串。
我只是偶然发现了这一切。我正在制定另一个测试方案,我在 design-time string builder 中混淆了用户名和密码值。连接失败报告表明这次登录失败,显示尝试的用户名。等一下!那是我的密码!呸
唯一可以生成 design-time 字符串的方法(在那个版本的代码下)是在调用默认构造函数的过程中。根据我之前的理解,这不是在运行时发生的。
生活和学习。
这是直接的代码,以防在遥远的将来的某个时候我决定取消回购协议。可能,但不太可能。我宁愿把它作为有问题的编程实践的纪念品。
旧上下文(损坏)
Imports System.Data.Common
Imports System.Data.Entity
Imports System.Data.SqlClient
Imports System.Reflection
Imports DbConnectionTest.Db.Models
Namespace Db
Public Class Context
Inherits DbContext
Public Sub New()
MyBase.New(Utils.DesignTimeConnectionString) ' <--
End Sub
Private Sub New(Connection As DbConnection)
MyBase.New(Connection, True)
Database.SetInitializer(New CreateDatabaseIfNotExists(Of Context))
Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of Context, Migrations.Configuration))
Me.Database.Initialize(False)
End Sub
Public Shared Function Create() As Context
Return New Context(New SqlConnection(Utils.RunTimeConnectionString)) ' <--
End Function
Protected Overrides Sub OnModelCreating(Builder As DbModelBuilder)
Builder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly)
MyBase.OnModelCreating(Builder)
End Sub
Protected Overrides Sub Dispose(Disposing As Boolean)
MyBase.Dispose(Disposing)
End Sub
Public Overridable Property Customers As DbSet(Of Customer)
Public Overridable Property Invoices As DbSet(Of Invoice)
End Class
End Namespace
新实用程序(正在运行)
Imports System
Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient
Namespace Db
Friend Class Utils
Friend Shared ReadOnly Property DbConnectionString() As String
Get
If ConfigurationManager.ConnectionStrings Is Nothing Then
With New SqlConnectionStringBuilder
.MultipleActiveResultSets = True
.PersistSecurityInfo = False
.IntegratedSecurity = False
.InitialCatalog = DB_NAME
.DataSource = Environment.MachineName
.Password = ""
.UserID = ""
DbConnectionString = .ConnectionString
End With
Else
DbConnectionString = ConfigurationManager.ConnectionStrings(DB_NAME).ConnectionString
End If
End Get
End Property
Friend Const DB_NAME As String = "DbConnectionTest"
End Class
End Namespace