我正在寻找使用最少量样板代码执行数据库查询的最佳方法。 SqlCommand documentation中建议的方法:
private static void ReadOrderData(string connectionString)
{
string queryString = "SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}", reader[0], reader[1]));
}
}
finally
{
reader.Close();
}
}
}
主要由必须在与数据库交互的每个方法中重复的代码组成。
我已经习惯于分解连接的建立,这将产生更像下面的代码。 (我也在修改它以便它返回数据,以便使示例变得不那么简单。)
private SQLConnection CreateConnection()
{
var connection = new SqlConnection(_connectionString);
connection.Open();
return connection;
}
private List<int> ReadOrderData()
{
using(var connection = CreateConnection())
using(var command = connection.CreateCommand())
{
command.CommandText = "SELECT OrderID FROM dbo.Orders;";
using(var reader = command.ExecuteReader())
{
var results = new List<int>();
while(reader.Read()) results.Add(reader.GetInt32(0));
return results;
}
}
}
这是一个改进,但仍然有足够的样板来唠叨我。这可以进一步减少吗?特别是,我想对程序的前两行做些什么。我不认为该方法应该负责创建SqlCommand
。这是一个很小的重复,就像在示例中一样,但如果交易是手动管理或超时被更改或类似的事情,它似乎会增长。
编辑:假设,至少假设,必须返回一堆不同类型的数据。因此,解决方案不能只是一种一刀切的方法,至少需要有几个不同的方法,取决于ExecuteNonQuery
,ExecuteScalar
,{{1 }},ExecuteReader
或其他任何人都被调用。我想减少那些重复。
答案 0 :(得分:5)
尝试Dapper?
当然,这不会让你成为DataReader,但是一旦你尝试过,你可能会更喜欢这种方式。
这是ORM最轻的重量,同时仍被称为ORM。没有更多方法可以在DataReader和强类型之间进行映射。
在所有StackExchange网站上使用。
using (var conn = new SqlConnection(cs))
{
var dogs = connection.Query("select name, age from dogs");
foreach (dynamic dog in dogs)
{
Console.WriteLine("{0} age {1}", dog.name, dog.age);
}
}
或
using (var conn = new SqlConnection(cs))
{
var dogs = connection.Query<Dog>("select Name, Age from dogs");
foreach (Dog dog in dogs)
{
Console.WriteLine("{0} age {1}", dog.Name, dog.Age);
}
}
class Dog
{
public string Name { get; set; }
public int Age { get; set; }
}
答案 1 :(得分:2)
如果您想自己滚动数据访问,这种帮助方法模式可能是消除重复的一种方法:
private List<int> ReadOrderData()
{
return ExecuteList<int>("SELECT OrderID FROM dbo.Orders;",
x => x.GetInt32("orderId")).ToList();
}
private IEnumerable<T> ExecuteList(string query,
Func<IDataRecord, T> entityCreator)
{
using(var connection = CreateConnection())
using(var command = connection.CreateCommand())
{
command.CommandText = query;
connection.Open();
using(var reader = command.ExecuteReader())
{
while(reader.Read())
yield return entityCreator(reader);
}
}
}
你必须添加对参数的支持,这可能无法编译,但这个模式正是我试图说明的。
答案 2 :(得分:1)
我通常使用的是我之前写过的一个自定义类,它接受一个SQL字符串,可选择一个参数列表,它返回一个DataTable。
由于调用之间的变化通常只是最佳的恕我直言的SQL。
如果您确实需要使用DataReader,可以执行以下操作:
public void ExecuteWithDataReader(string sql, Action<DataReader> stuffToDo) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
using (SqlCommand command = new SqlCommand(sql, connection)) {
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
try {
while (reader.Read()) {
stuffToDo(reader);
}
}
finally {
reader.Close();
}
}
}
}
}
private static void ReadOrderData(string connectionString) {
string sql = "SELECT OrderID, CustomerID FROM dbo.Orders;";
ExecuteWithDataReader(sql, r => Console.WriteLine(String.Format("{0}, {1}", r[0], r[1])));
}
答案 3 :(得分:0)
前两行是你需要的最重要的事情......
但是如果你仍然希望这样做,你可以把它们变成一个数据库处理程序类,是的,它会变成更多的代码,但在重构概念中,每一件事都会转移到相关的主题......
尝试编写一个单例类,接收命令并执行操作,因此返回SqlDataReader类型的结果...
答案 4 :(得分:0)
在评论中这样做太多了。
我建议使用样板代码
using(conn = new sqlconnection)
using(cmd = new sqlcommand) {
// blah blah blah
}
不是轻微删除的东西,而是鼓励你将它保持在原来的位置。资源,尤其是非托管资源,应尽可能在最近的地点打开和发布,以便尽快执行。
在很大程度上,由于其他开发人员很难遵循相应的清理惯例。
如果您执行类似
的操作private SQLConnection CreateConnection()
{
var connection = new SqlConnection(_connectionString);
connection.Open();
return connection;
}
然后,您正在邀请另一个程序员调用此方法,并在执行查询后立即完全无法释放资源。我不知道你正在构建什么样的应用程序,但是在Web应用程序中,这样的事情会导致难以调试的类型的内存/连接/资源错误,除非你之前已经通过它。
相反,我建议您查看一个轻量级的ORM,例如Dapper.net或类似的,看看他们是如何接近它的。我不使用短小精悍,但我听说它非常好。我不使用它的原因很简单,我们不允许对我们的数据库执行内联sql(但这是一个非常不同的对话)。
这是我们的标准:
public static DataTable StatisticsGet( Guid tenantId ) {
DataTable result = new DataTable();
result.Locale = CultureInfo.CurrentCulture;
Database db = DatabaseFactory.CreateDatabase(DatabaseType.Clients.ToString());
using (DbCommand dbCommand = db.GetStoredProcCommand("reg.StatsGet")) {
db.AddInParameter(dbCommand, "TenantId", DbType.Guid, tenantId);
result.Load(db.ExecuteReader(dbCommand));
} // using dbCommand
return result;
} // method::StatisticsGet
我们大量使用企业库。它简短,简单,经得起考验。这个方法只返回一个数据表但你很容易让它返回一个对象集合..或者没有。