【问题标题】:ConnectionString lost on second executeConnectionString 在第二次执行时丢失
【发布时间】:2018-04-06 22:42:07
【问题描述】:

我正在使用 asp.net core 2.0 和 dapper。我有一个包装 IDbConnection 接口并且只公开某些方法的类。这是该类的简短版本。

public class MyConnectionString : IMyConnectionString
{
    private readonly IDbConnection _connection;

    public int ConnectionTimeout => _connection.ConnectionTimeout;
    public string Database => _connection.Database;
    public string ConnectionString { get => null; set => _connection.ConnectionString = value; }
    public ConnectionState State => _connection.State;

    public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
    {
        var con = "some logic to get the connection string.";
        _connection = new SqlConnection(con);
    }

    public int Execute(string query, object parameters = null)
    {
        using (var con = _connection) { return con.Execute(query, parameters); }
    }
}

我通过构造函数将这个类注入到我的存储库服务中。例如,这是一个可以调用它的方法:

internal class SomeRepository
{
    private readonly IMyConnectionString _connection;

    public SomeRepository(IMyConnectionString connection)
    {
        _connection = connection;
    }

    public void ExecuteSomeQuery(Object params)
    {
        var query = "Some query...";
        _connection.Execute(query, params);
    }
}

现在的问题是,如果我在一个请求(2 个不同的服务)中调用 _connection.Execute(query, params); 两次,第二次调用 ConnectionString 类中的 ConnectionString 值是空的。我已经尝试在 Transient 和 Request 范围内绑定它,看看它是否会保留它,但没有运气。知道为什么会发生这种情况,或者我如何保存它,这样我就不必在每次请求时都创建连接字符串?

【问题讨论】:

  • “MyConnectionString 类中的ConnectionString 值是空的”...ConnectionString { get =&gt; return null; ...,除了null,你希望它返回什么?
  • 同样using (var con = _connection) 在第一次执行后有效地处理_connection
  • 您应该在每个请求上创建一个新连接。由于连接池,几乎不会有任何开销。此外,使用 Facade 仅公开某些 Dappers 方法的目的是什么?我看不出这有什么好处。
  • @NikolajDamLarsen 并非所有IDbConnection 对象都很便宜——连接池仅适用于 SQL Server 和可能的其他第三方连接。例如,与 Access/JET 数据库的连接很昂贵,并且应该是长期存在的。
  • @Dai 当然可以,但根据他的问题,我可以安全地假设他正在使用 Sql Server,因为他正在创建 SqlConnection 对象。

标签: c# dependency-injection asp.net-core asp.net-core-mvc


【解决方案1】:

Connection 包裹在using 中会在执行结束时处理Connection:就像@Jasen 在cmets 中所说的那样。

在您的情况下,我会获取连接并在构造函数中创建的Connection 上执行:完全删除using

你不应该创建SqlConnection,因为你正在实现依赖注入。你应该:

  • 实施IDisposable 以在收集您的课程时释放您的连接。
  • 通过SqlConnection 工厂来创建您的SqlConnection,将您的创建逻辑与您的类分开。

你的类应该是这样的:

public class MyConnectionString : IMyConnectionString
{
    private readonly IDbConnection _connection;

    public int ConnectionTimeout => _connection.ConnectionTimeout;
    public string Database => _connection.Database;
    public string ConnectionString 
    {     
        get => null; 
        set => _connection.ConnectionString = value; 
    }

    public ConnectionState State => _connection.State;

    public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
    {
        string con = "some logic to get the connection string.";
        _connection = new SqlConnection(con);
    }

    public int Execute(string query, object parameters = null)
    {
        return _connection.Execute(query, parameters);
    }
}

使用IDisposable 实现:

using System;
public class MyConnectionString : IMyConnectionString, IDisposable
{
    private readonly IDbConnection _connection;

    public int ConnectionTimeout => _connection.ConnectionTimeout;
    public string Database => _connection.Database;
    public string ConnectionString 
    {     
        get => null; 
        set => _connection.ConnectionString = value; 
    }

    public ConnectionState State => _connection.State;

    public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
    {
        string con = "some logic to get the connection string.";
        _connection = new SqlConnection(con);
    }

    public int Execute(string query, object parameters = null)
    {
        return _connection.Execute(query, parameters);
    }

    public void Dispose()
    {
        _connection.Dispose();
    }
}

用你自己的ISqlConnectionFactory工厂:

public class MyConnectionString : IMyConnectionString, IDisposable
{
    private readonly IDbConnection _connection;
    private readonly ISqlConnectionFactory _factory;

    public int ConnectionTimeout => _connection.ConnectionTimeout;
    public string Database => _connection.Database;
    public string ConnectionString 
    {     
        get => null; 
        set => _connection.ConnectionString = value; 
    }

    public ConnectionState State => _connection.State;

    public MyConnectionString(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper, ISqlConnectionFactory factory)
    {
        _factory = factory;
        _connection = _factory.CreateConnection(connProvOpts, encHelper);
    }

    public int Execute(string query, object parameters = null)
    {
        return _connection.Execute(query, parameters);
    }
}

public interface ISqlConnectionFactory
{
    SqlConnection CreateConnection(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper);
}

public class SqlConnectionFactory : ISqlConnectionFactory
{
    public SqlConnectionFactory()
    {
         // Maybe initialization?
    }

    public SqlConnection CreateConnection(IOptions<ConnectionProviderOptions> connProvOpts, EncryptionHelper encHelper)
    {
        string con = "some logic to get the connection string.";
        _connection = new SqlConnection(con);
    }
}

就个人而言,我会在每次调用 Execute 时创建并处置 Connection。这意味着在Execute 之外,您的连接将关闭并释放资源。

【讨论】:

  • 如果你这样做,你应该注入SqlConnection或实现IDisposable
  • 没错,我想知道他的连接字符串是从哪里来的。肯定来自他的构造函数参数。
  • @Bojan Connections 应该尽可能少地存在。您可以将解密的值存储在内存中并丢弃连接
  • 将常量值而不是解密/处理/构建的值存储为private static 字段是很糟糕的。如果我愿意,无论如何我都可以从_connection 字段中读取它
  • MyConnectionString 类中保持连接活动可能会产生不幸的后果,具体取决于它在 IoC 容器中的注册方式。我绝对同意您应该在使用连接后立即处理它,如果只是为了释放池中的连接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-01
  • 1970-01-01
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多