另一个 C# SQL 类






4.80/5 (23投票s)
另一篇关于 C# .NET SQL 的文章,但更智能
引言
像大多数其他业务开发人员一样,我写过不少用于某种形式数据库交互的程序。加载这一堆数据,转换它,然后把它推到另一个地方。或者,另一种情况是,加载带有这些过滤条件的数据,进行编辑渲染,然后保存更改。每个业务开发人员,在某个时候,都会做这些步骤的某种形式或变体。
然而,我们完成这项工作的方式似乎有些不同。我的意思是 .NET 有很棒的 System.Data.SqlClient 命名空间来真正满足我们 SQL Server 交互的需求。但是,对我来说,它真的非常底层。它需要大量的设置才能启动任何东西。
// I mean, look at the setup JUST to get this going:
var con = new SqlConnection("connectionString");
var cmd = new SqlCommand()
{
CommandText = "SELECT col_one FROM tb_table WHERE col_id = @id",
CommandType = CommandType.Text,
CommandTimeout = 30, // Optional, but included for the sake of argument
Connection = con
};
var datareader = cmd.ExecuteReader(true); // close the connection
// Now have fun converting the Reader to a DataSet
对我来说,这似乎是很多前期工作,只是为了查询一些信息。然而,就像我一样,我敢肯定大多数开发人员都有某种类来抽象掉一些东西。好吧,今天,我要向大家展示我的解决方案:SQLMagic。
什么是 SQLMagic?
SQLMagic 最初是一个小型项目,旨在简化我的 C# 代码,以便我可以专注于一次编写查询(在你喜欢的编辑器中。我偏爱 SQL Server Management Studio),将它们放入我的应用程序,执行它们,然后继续。我不想花太多时间设置我的应用程序环境来与我的 SQL Server 交互。我想要一个我永远可以引用、创建和使用的简单类。于是,SQLMagic 应运而生。
目标
SQLMagic 的设计初衷就是简单。SQLMagic 的工作方式应该一目了然,永远如此。它应该能够处理我需要的一切,而不会像 SQL Server 通常会抛出的那样引起任何不必要的麻烦。因此,SQLMagic 拥有相当多的方法来实现这一目标。99% 的人会以简单的方式使用它,而无需深入研究。然而,这篇文章将包含一个更高级的部分,以防万一。
使用代码
SQLMagic 就是这么简单。这就是它的要点。因此,使用它也很简单
// Declare once, keep using
var nTimeout = 30; // timeout
var bLog = true;
// Create with a timeout of 30 seconds and logging as true
var oSql = new Sql("Connection String", nTimeout, bLog);
// Now let's roll!
SqlResultWithDataSet oResult = oSql.Open("SELECT * FROM tbl");
就是这样。打开 DataSet 所需的就是这些!
但是,如果你听从大多数 DBA(以及有常识的开发人员)的建议,你会意识到你*真的*应该在查询中使用参数。放心,SQLMagic 已经为你准备好了。
// I'm going to re-use oSql and oResult from above ^^^
oResult = oSql.Open("SELECT * FROM tbl WHERE tbl_id = @id",
CommandType.Text,
new SqlParameter("id", 100)
);
SQLMagic 使*编写正确的查询*变得极其简单
oResult = oSql.Open("SELECT * FROM tbl WHERE tbl_id IN (@id1, @id2, @id3)",
CommandType.Text,
new SqlParameter("id1", 1),
new SqlParameter("id2", 2),
new SqlParameter("id3", 3)
);
你需要多少个参数*并不重要*。SQLMagic 使这一切变得简单而轻松。
这涵盖了大多数用例。然而,SQLMagic 由于利用了 SqlClient 命名空间,可以实现更多功能。
// Stored Procedures with parameters?
oSql.Execute("sp_GoDoSomethingWithParameters",
CommandType.StoredProcedure,
new SqlParameter("param1", "value!")
);
// What about return values?
var datetime = oSql.Execute<DateTime>("SELECT GETDATE()");
var intBack = oSql.Execute<Int32>("SELECT 1");
// Stored Procedure that has a return value?
var intBackAgain = oSql.Execute<Int32>("sp_GiveNumber", CommandType.StoredProcedure);
SQLMagic 使所有这些都极其简单易用。
高级程序员:使用代码
本节将介绍 SQLMagic 的一些更高级的功能。这并不一定意味着这些主题*很困难*,但它们肯定会是更高级的开发人员会使用到的。
异步支持
SQLMagic 在设置了 "NET45" 编译标志后,支持 .NET 的 async/await 模型,并以一种非常简洁、易于使用的方式实现它。
// Converting is VERY easy!
var oResult = await oSql.OpenAsync("SELECT * FROM aVeryLargeTable");
var nValue = await oSql.ExecuteAsync<Int32>("SELECT bigNumberComputationThatResultsInInteger");
var nProcedure = await oSql.ExecuteAsync<Int32>("sp_LongRunningProcedure",
CommandType.StoredProcedure
);
完成!你现在正在使用 .NET 4.5+ 的 async/await 功能。SQLMagic 通过更改几行代码,就可以支持同步*或*异步模式,并且具有完全相同的重载(顺序正确)。
事务支持
SQLMagic 使你能够支持或不支持异步功能地开始、提交和回滚事务。
// Start a Transaction:
SqlTransaction oTransaction = oSql.BeginTransaction();
// The asynchronous version:
SqlTransaction oTransaction = await oSql.BeginTransactionAsync();
// one of the overloads that SQLMagic has is the ability to specify a transaction!
// public SqlResultWithDataSet Open(Statement aStatement, SqlConnection aConnection, Boolean aCloseConnection, SqlTransaction aTransaction)
// Statement is merely a SQLMagic struct that groups up some parameters from earlier:
Statement oStatement = new Statement
{
Sql = "INSERT INTO tbl VALUES(@val1)",
Type = CommandType.Text,
Parameters = new List<SqlParameters>() { new SqlParameter("val1", "value") }
};
oSql.Open(oStatement, oTransaction.Connection, false, oTransaction);
// Open a DataSet of the given statement, connection, don't close it, and use that transaction
// Once you're all done, you end!
oSql.EndTransaction(oTransaction, true); // true or false indicates COMMIT or ROLLBACK!
// The asynchronous version:
await oSql.EndTransactionAsync(oTransaction);
手动连接创建
SQLMagic 允许你自己创建连接(请记住 SQLMagic 有表示 SqlConnection 对象的重载!)。
// Synchronous
SqlConnection oConnection = oSql.CreateConnection(true);
// Asynchronous
SqlConnection oConnection = await oSql.CreateConnectionAsync(true);
// The boolean value indicates whether or not the connection is automatically opened when created
TryOpen/TryExecute/TryExecute<T>
SQLMagic 可以*尝试*为你执行查询,而不是抛出异常,它只会返回一个布尔值表示成功,并使用一个 out 参数来存储你的结果。
// Variables
SqlResultWithDataSet oResult;
if (oSql.TryOpen("SELECT * FROM tbl WHER tbl.id = 1", out oResult))
{
// This fails because of syntax ^
}
else
{
// Because it failed, you can handle the exception here!
MessageBox.Show(oResult.Exception.ToString());
}
下一步?
我还有一些事情想在 SQLMagic 中实现。首先,一个类似 Fluent 的接口会很不错。
// This would be somewhat
var oResult =
new SqlMagic("connectionString")
.Select("column1", "column2")
.From("tbl")
.Where("x", Operators.GreaterThan, "1")
.And("y", Operators.LessThan, "2")
.Execute();
然而,这一点尚未完全考虑,因为我不想干涉或取代 LINQ-To-SQL 所提供的功能。
我还开始开发一个观察者类,它记录对各种命令和连接活动的观察。它作为私有的静态类存在于 SQLMagic 的主类中。但这可能会发生变化。
重大更改
该库可能会进行破坏性更改,以整合新功能或规范化命名空间。这只是一个小小的警告。
关注点
async/await 最初实现起来有点奇怪。我一直遇到程序卡死的情况。上下文捕获有时很难。
我的同事以前是一名 DBA,他帮助我确保面向公众的 API 有意义,并且一直在与我沟通我应该添加哪些功能,或者哪些功能看起来有用。
SQLMagic 还依赖一个 NuGet 包:Microsoft 的 不可变集合库。
历史
2014/4/1:初始发布 1.0