【问题标题】:The ConnectionString property initializationConnectionString 属性初始化
【发布时间】:2019-10-10 14:22:27
【问题描述】:

虽然这个问题似乎已经有了答案,但我的情况不同,这就是方法。

第一次有效,但后续请求失败。

我在主类中创建连接,并将其作为构造函数中的依赖项传递给 DB 类,它旨在为每次调用重复使用。

public class DB
{
    private SqlConnection conn;

    public DB(SqlConnection conn)
    {
        this.conn = conn;
    }

    public List<Records> GetRecords()
    {
        using (conn){
            conn.Open();
            using (SqlCommand cmd = new SqlCommand("SELECT * FROM Records", conn))
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                List<Records> rows = new List<Records>();
                while (reader.Read())
                {
                    rows.Add(new Records(reader.GetString(1)));
                }
                return rows;
            }
        }
    }
}

调用者类

string connection = $@"
    Data Source=;
    Initial Catalog=;
    Persist Security Info=True;
    User ID={env["DATABASE_USER"]};
    Password={env["DATABASE_PASSWORD"]};";

Db db = new DB(new SqlConnection(connection));
db.GetRecords();

失败: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] 执行请求时发生未处理的异常。 System.InvalidOperationException:ConnectionString 属性具有 未初始化。

【问题讨论】:

  • 不恰当地重用 SqlConnection 对象,错误地认为创建它们的成本很高是很常见的。他们不是; SqlConnection 对象表示池物理连接的句柄。只有第一个创作是昂贵的。很少有充分的理由将 SqlConnection 实例放在字段中,更不用说在范围之间传递它们了。反对重用连接的最重要论点是,如果发生错误,它们将变得不可用,需要关闭。如果连接是共享的,这会使错误恢复成为一场噩梦。
  • @JeroenMostert 我也意识到我为什么首先这样做,这样我就可以模拟我用其他语言完成的单元测试中的依赖关系,这是 csharp 的新手,所以不知道如何接近那个。
  • 两种显而易见的方法:您可以使用 LocalDB 实例(实际上,保持数据库代码不变,并为其提供功能齐全的本地数据库),或者在一个类中重构数据库代码生成数据,调用者不需要知道它来自数据库,并对其进行模拟。人们偶尔会尝试“中间”并保留来自System.Data 的对象,而您仍然以某种方式拦截物理数据库访问,但即使有可能,由于强耦合,这往往比显而易见的方法复杂得多在这些类之间。
  • 此外,如果您的数据需求相当琐碎(如本例中所示),请考虑使用像 Dapper 这样的简单 ORM 对数据库进行抽象。这为您在序列化对象时节省了大量样板代码,并且生成 IEnumerable&lt;...&gt; 实例的方法很容易模拟。
  • 谢谢,我喜欢第二个选择,这意味着我会创建一个 FakeDB 类而不是 DB 类并生成记录。

标签: c# asp.net ado


【解决方案1】:

我不是 100% 确定,但我想问题出在

using(conn)

using 关闭时,SqlConnection 将被释放。
因此,当您再次调用 db.GetRecords(); 时,
conn.Open() 未初始化。 -> 异常

【讨论】:

    【解决方案2】:

    您不应将SQLConnection 用作字段,而应将其用作方法内的局部变量。更改您的类以接收其参数中的连接字符串,而不是 SqlConnection 的实例,并在使用它的任何方法中对其进行初始化:

    public class DB
    {
        private string connectionString;
    
        public DB(string connectionString)
        {
            this.connectionString = connectionString;
        }
    
        public List<Records> GetRecords()
        {
            using (var conn = new SqlConnection(connectionString)){
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("SELECT * FROM Records", conn))
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    List<Records> rows = new List<Records>();
                    while (reader.Read())
                    {
                        rows.Add(new Records(reader.GetString(1)));
                    }
                    return rows;
                }
            }
        }
    }
    

    更多详情,read this.

    【讨论】:

    • 我可以保持连接打开并重新使用请求吗?那会不会效率低下,如果那样的话,为什么会效率低下。我能想到的唯一原因可能是它会将所有调用排队,并且每个请求都必须等待较早的请求完成,对吧?
    • @AppDeveloper:一个连接支持不超过一个活动命令(除非使用多个活动结果集,它有自己的警告)并且一个命令在完成之前不会返回,所以无论你重新使用连接或关闭并重新打开它与此无关。
    • 连接池确保释放和重新创建连接是一个非常便宜的过程。我的答案底部的链接解释了这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-11
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多