【发布时间】: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<...>实例的方法很容易模拟。 -
谢谢,我喜欢第二个选择,这意味着我会创建一个
FakeDB类而不是DB类并生成记录。