【问题标题】:C# fetch connection details inside a constructorC# 在构造函数中获取连接详细信息
【发布时间】:2021-09-25 12:32:21
【问题描述】:

我的 DB 类定义如下:

public class DbAdapterService : DbAdapterService
{
      private readonly AppSettings _settings;
      public DbAdapterService(IOptions<AppSettings> settings)
      {
           _settings = settings?.Value;
           DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
           builder.ConnectionString = _settings.ConnectionString;           
      }
}

在上面的类中,我从我的 appsettings.json 中获取我的连接字符串,它工作正常。

现在我们需要从我们类中定义的另一个方法中获取连接字符串用户名和密码。此方法从我们存储的保管库中获取这些详细信息。示例如下:

public class CredentialsService : ICredentialsService 
       {   
         public Credentials GetDetails()
         {
            //return credentials
         }
       }

我的问题是我可以在上面的 DbAdapterService 构造函数中调用这个方法吗,或者是否有更好的方法来处理这个问题。

谢谢

--更新--

public class DbAdapterService : DbAdapterService
{
      private readonly AppSettings _settings;
       public ICredentialsService  _credentialsService;
       private bool isInitialized = false;
      
      public DbAdapterService(IOptions<AppSettings> settings, ICredentialsService  credentialsService)
      {
           _settings = settings?.Value;
           
           _credentialsService = credentialsService;
            if (!isInitialized)
            {
                Initialize(_credentialsService);
            }
           
           DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
           builder.ConnectionString = _settings.ConnectionString;           
      }
      
       public void Initialize(ICredentialsService  credentialsService)
        {
            if (isInitialized)
                return;
            else
            {
               //credentialsService.GetDetails();
                isInitialized = true;
            }          
            
        }
}

【问题讨论】:

  • 一般构造函数不应该接触其他服务。如果 DbAdapterService 需要从其他地方获取连接详细信息,它应该有一个可以调用的 Initialize(或 InitializeAsync)方法,并且该方法可以访问 API 或数据库或任何地方以获取所需的值。
  • @GertArnold 没错,我从来没有说过不是。恐怕我不明白你评论的重点。你能解释一下你为什么这么说吗?
  • @GertArnold 访问另一个服务(例如 API 或数据库)是一种固有的危险操作,通常需要异步完成。这种逻辑不属于构造函数体。见Meligy's answer here。自从编写了该答案以来,异步代码变得更加多产,并且从构造函数调用异步代码并不是一种令人愉快的方式。这里的问题中有一行“`此方法从我们存储的保险库中获取这些详细信息。” - 我们还没有看到它的细节,但它可能是异步的
  • @mason 能否分享一个示例,说明如何使用 Initialize 方法和构造函数来获取这些详细信息
  • 它不会在构造函数中。这就是重点:构造函数不是处理这类事情的好地方。相反,您可以将该逻辑放入一个方法中,然后您可以调用该方法从您需要的任何外部来源获取详细信息。

标签: c# asp.net .net asp.net-core .net-core


【解决方案1】:

您似乎想在构造函数中初始化连接字符串。如果您要使用某些外部组件(例如文件系统、数据库或 API)来检索值,则可能是异步操作。在构造函数中调用异步方法没有可靠的方法。

那么,我们能做些什么呢?好吧,没有规则说我们必须在构造函数中这样做。构造函数是一个方便的地方,因为它确保在您调用任何其他方法时,初始化已经发生。但是还有其他模式可以实现这一点。这是一个:

public class DbAdapterService : IDbAdapterService
{
    string _connectionString;

    readonly AppSettings _settings;
    readonly ICredentialsService _credentialsService;
    
   public DbAdapterService(IOptions<AppSettings> settings,
                           ICredentialsService  credentialsService)
   {
       _settings = settings.Value;         
       _credentialsService = credentialsService;         
   }

   async Task EnsureInitializedAsync()
   {
       if (!string.IsNullOrEmpty(_connectionString))
       {
           //no need to reinitialize
           //unless the credentials might change during the lifecycle of this object
           return; 
       }

       var credentials = await _credentialsService.GetDetailsAsync();
       var builder = new DbConnectionStringBuilder(_settings.ConnectionString);
       builder.Username = credentials.Username;
       builder.Password = credentials.Password;
       _connectionString = builder.ConnectionString;         
   }

   public async Task DoSomethingAsync()
   {
       await EnsureInitializedAsync();

       //now you can use _connectionString here
   }
}

关键部分是记住在任何需要使用连接字符串的方法中调用 EnsureInitializedAsync()。但至少使用 DbAdapterService 的代码不必知道是否初始化连接字符串。

虽然这种模式对于非异步代码来说不是必需的,但它非常适合将来可能变为异步的操作,如果连接的细节实际上可能在运行时发生变化,那么这种模式更有意义,但是你的对象在配置 IoC 容器时构建。

【讨论】:

  • 谢谢这更有意义。
【解决方案2】:

你可以试试这个

public class DbAdapterService : DbAdapterService
{
      private readonly AppSettings _settings;
      private readonly ICredentialsService _credentialsService ;

      public DbAdapterService(
     IOptions<AppSettings> settings, 
      ICredentialsService credentialsService )
      {
          _credentialsService= credentialsService ;

           _settings = settings?.Value;
           DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
           builder.ConnectionString = _settings.ConnectionString;           
      }
}

在此之后,您可以从 credentialService 调用任何方法

如果您只需要凭据,则可以短一些

      private readonly AppSettings _settings;
     private readonly Credentials _credentials;

      public DbAdapterService(
     IOptions<AppSettings> settings, 
      ICredentialsService credentialsService )
      {
        _credentials= credentialsService.GetDetails();

           _settings = settings?.Value;
           DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
           builder.ConnectionString = _settings.ConnectionString;           
      }

【讨论】:

  • 所以可以注入服务,然后在构造函数中调用它的方法
  • @Brenda 是的,如果您只需要凭据,则只能调用一种方法。
猜你喜欢
  • 2018-02-20
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
  • 2016-10-22
  • 2017-10-08
相关资源
最近更新 更多