65.9K
CodeProject 正在变化。 阅读更多。
Home

另一个 C# SQL 类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (23投票s)

2014年4月2日

CPOL

4分钟阅读

viewsIcon

50675

downloadIcon

1729

另一篇关于 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

© . All rights reserved.