如何使NServiceBus基于主机的Windows服务等待SQL Server启动?

时间:2011-12-19 03:27:07

标签: nservicebus

我在与包含我的订阅存储数据库的SQL Server实例相同的服务器上安装了几个基于NServiceBus通用主机的Windows服务。当服务器引导我的NServiceBus主机服务时,尝试在SQL Server启动之前访问订阅存储数据库。

这是我到目前为止所尝试的内容:

  1. 在MSSQLSERVER上添加了Windows服务器依赖项。这并没有解决问题。
  2. 在IWantCustomInitialization.Init中添加1分钟睡眠,以验证上述问题的原因。这解决了这个问题,但这是一个非常粗糙的解决方案。
  3. 处理此问题的推荐方法是什么?

    供参考查找我的DBSubscriptionStorageConfig以及我的Windows服务尝试在下面启动时发生的异常。

    <DBSubscriptionStorageConfig>
    <NHibernateProperties>
      <add Key="connection.provider" Value="NHibernate.Connection.DriverConnectionProvider"/>
      <add Key="connection.driver_class" Value="NHibernate.Driver.SqlClientDriver"/>
      <add Key="connection.connection_string" Value="Data Source=.;Initial  Catalog=NServiceBus;Integrated Security=SSPI"/>
      <add Key="dialect" Value="NHibernate.Dialect.MsSql2008Dialect"/>  
    </NHibernateProperties>
    </DBSubscriptionStorageConfig>
    
    
    ERROR NHibernate.Tool.hbm2ddl.SchemaUpdate [(null)] <(null)> - could not get database metadata
    System.Data.SqlClient.SqlException (0x80131904): Cannot open database "NServiceBus" requested by the login. The login failed.
    Login failed for user 'SE1\fooservice'.
    at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
    at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
    at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler,            
    SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
    at System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK)
    at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, TimeoutTimer timeout)
    at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(SqlConnection owningObject,     
    TimeoutTimer timeout, SqlConnectionString connectionOptions, String newPassword, Boolean redirectedUserInstance)
    at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity,         
    SqlConnectionString connectionOptions, Object providerInfo, String newPassword, SqlConnection owningObject, Boolean redirectedUserInstance)
    at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options,       
    Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection)
    at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options)
    at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject)
    at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject)
    at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
    at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
    at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection,       
    DbConnectionFactory connectionFactory)
    at System.Data.SqlClient.SqlConnection.Open()
    at NHibernate.Connection.DriverConnectionProvider.GetConnection()
    at NHibernate.Tool.hbm2ddl.ManagedProviderConnectionHelper.Prepare()
    at NHibernate.Tool.hbm2ddl.SchemaUpdate.Execute(Action`1 scriptAction, Boolean doUpdate)
    

2 个答案:

答案 0 :(得分:1)

我最终创建了一个实现IWantCustomInitialization的类,以防止我的Windows服务在连接到数据库之前启动。该类应该添加一些日志代码,但它现在解决了我的问题。

这是代码。

public class DbDependencyChecker : IWantCustomInitialization
{
    private const int ConnectionRetries = 15;
    private const int TimeBetweenRetries = 60000;      
    private const string DefaultConnectionName = "DbDependency";
    private const string CustomConnectionNameKey = "DbDependencyConnectionName";

    public void Init()
    {
        var connectionName = ConfigurationManager.AppSettings[CustomConnectionNameKey];
        if (string.IsNullOrWhiteSpace(connectionName))
            connectionName = DefaultConnectionName;
        var providerName = ConfigurationManager.ConnectionStrings[connectionName].ProviderName;
        var connectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;

        if (string.IsNullOrWhiteSpace(providerName) || string.IsNullOrWhiteSpace(connectionString))
            return;

        for (var i = 0; i < ConnectionRetries; i++)
        {
            if (TryToConnect(providerName, connectionString))
                break;
            System.Threading.Thread.Sleep(TimeBetweenRetries);
        }
    }

    private static bool TryToConnect(string providerName, string connectionString)
    {
        try
        {
            using (var connection = CreateConnection(providerName, connectionString))
            {
                connection.Open();
            }
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }

    private static IDbConnection CreateConnection(string providerName, string connectionString)
    {
        var provider = DbProviderFactories.GetFactory(providerName);
        var connection = provider.CreateConnection();
        connection.ConnectionString = connectionString;
        return connection;
    }        
}

配置文件如下所示:

<connectionStrings>
  <add name="SomeDb" providerName="System.Data.SqlClient" connectionString="connection string" />
</connectionStrings>

<appSettings>
  <add key="DbDependencyConnectionName" value="SomeDb"/>
</appSettings>

或者像这样:

<connectionStrings>
  <add name="DbDependency" providerName="System.Data.SqlClient" connectionString="connection string"/>    
</connectionStrings>

答案 1 :(得分:0)

使用启动类型为自动(延迟启动)的Windows服务修复它吗?不会认为它需要一分钟的延迟,这可能比应用程序的内部更令人满意(特别是如果你有一天在另一台机器上有数据库并且没有在那里运行sql server。