【发布时间】:2009-06-15 15:57:05
【问题描述】:
假设你有一些简单的存储过程,例如
AddXYZ(param1, param 2...等)
getAllXYZ()
getXYZ(id)
从“数据库层”调用这些存储过程的“最佳实践”方式是什么
我不想使用 linq。只是简单的 c# 静态方法来说明如何做到这一点。
我正在使用 sqlserver。
【问题讨论】:
标签: c# database stored-procedures
假设你有一些简单的存储过程,例如
AddXYZ(param1, param 2...等)
getAllXYZ()
getXYZ(id)
从“数据库层”调用这些存储过程的“最佳实践”方式是什么
我不想使用 linq。只是简单的 c# 静态方法来说明如何做到这一点。
我正在使用 sqlserver。
【问题讨论】:
标签: c# database stored-procedures
我不认为您真的是指 static 方法,因为这将是一种非常非 OO 的方式来处理这样的事情,并且 C# 中没有内置这样的工具。但是,有标准的 ADO.NET 类可以让您在没有任何 ORM 包装或使用 DataSet 等的情况下执行此操作。-
如果您真的想手动调用存储过程并在没有任何 ORM 或标准化存储机制的情况下返回一组结果,这将是您最好的选择:
using(System.Data.IDbConnection conn = /*create your connection here*/)
{
using(System.Data.IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "AddXYZ";
// add your parameters here using cmd.CreateParameter() and cmd.Parameters.Add()
using(System.Data.IDbDataReader reader = cmd.ExecuteReader())
{
while(reader.Read())
{
// read your results row-by-row
}
}
}
}
您没有指定要通过哪个数据库引擎进行连接,因此我使用了通用接口,可以将您从中抽象出来。如果您愿意(尽管我通常不赞成这样做的代码),您可以使用特定于平台的类,这会使事情变得更容易,至少在添加参数方面(代码不像基于接口的方法那样冗长)
【讨论】:
这是你要找的吗?
SqlCommand cmd = new SqlCommand("AddXYZ", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@param1", someValue));
【讨论】:
首先,您在数据层中创建一个用于获取连接信息的地方。如果您的数据层仅限于单个类,则这可能是私有成员;如果该层包含整个程序集,则可能是内部项。它可以返回一个连接字符串或一个实际的连接对象本身,但主要的是它根本没有暴露在数据层之外:
private static ConnectionString { get { // read from config file once.... return ""; } }
private SqlConnection getConnection()
{
SqlConnection result = new SqlConnection(ConnectionString);
result.Open(); // I like to open it in advance, but that's less common
return result; // you'll want some error handling code in here as well
}
然后,您在数据层中提供与您要提供给业务层的接口相匹配的公共方法。在精心设计的应用程序中,这通常会与存储过程相匹配,这听起来像是您想要的,但有时效果并不好。例如,您可能需要从一种方法调用多个过程。
无论您做什么,该方法都应该接受强类型参数值,以便在调用过程时使用。关于该方法是否应该返回业务对象或数据记录存在一些争论。就个人而言,我倾向于返回数据记录,但提供一个额外的“层”,将数据记录转换为强类型业务对象:
public IDataRecord GetXYZ(int id)
{
DataTable dt = new DataTable();
using (var cn = getConnection())
using (var cmd = new SqlCommand("getXYZ"))
{
cmd.CommandType = CommandTypes.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Int).Value = id;
using (var rdr = cmd.ExecuteReader())
{
dt.Load(rdr);
}
}
//obviously put a little more work into your error handling
if (dt.Rows.Count <= 0)
throw new Exception("oops");
return dt.Rows[0];
}
public class XYZFactory
{
public static XZY Create(IDataRecord row)
{
XYZ result = new XYZ();
result.id = row["ID"];
result.otherfield = row["otherfield"];
return result;
}
}
【讨论】:
一般来说,您需要查看 SQLConnection 和 SQLCommand 类(当然,假设您正在连接到 SQL 数据库)。您将 SQLCommand.CommandText 属性设置为类似“EXEC AddXYZ @X, @Y, @Z”,然后对 @X、@Y 和 @Z 中的每一个使用 SQLCommand.Parameters.AddWithValue()。
然后您将调用 SQLCommand 的适当执行方法(NonQuery、Scalar 或 Reader)。
【讨论】:
Thomas 的示例是标准方法。我建议您查看以下内容以获得更多帮助:
http://msdn.microsoft.com/en-us/library/aa902662.aspx
就我个人而言,我编写了一个静态类 (DataHelper),它公开了一堆您可能会使用的方法,例如当您的存储过程返回内容时返回 IDataReader 对象的方法,以及当它们返回时的 void 方法不是,还有一些在 out 参数中返回 Connection 对象,以便您可以保持打开状态。
该类只是将 ADO.NET 代码包装在 try catch 块中,用于常见问题检查和日志记录/跟踪(DNS 关闭、服务器关闭、svc 关闭),并使用 using 语句确保不浪费资源。我还包装了一个使用 ConnectionStringBuilder 的方法来返回连接字符串,这样我就不必继续编写该代码了。
我确实发现,对于一个大型项目,对我所有的实体“提供者”进行编码是重复的,我应该设计一个更可重用的配置驱动模型。
这些天,如果我有新的领域,我会考虑使用 EDM。
http://msdn.microsoft.com/en-us/library/aa697428(VS.80).aspx
【讨论】: