DRY CLR表值函数
本文关键字:函数 CLR DRY | 更新日期: 2023-09-27 17:59:42
我使用CLR表值函数来选择并返回使用许多变量的复杂数据库搜索的结果。
文档显示您以类似于以下的方式构建这样一个函数:
public partial class UserDefinedFunctions
{
private class ResultRow
// This class holds a row which we want to return.
{
public SqlInt32 CustId;
public SqlString Name;
public ResultRow(SqlInt32 custId_, SqlString name_)
{
CustId = custId_;
Name = name_;
}
}
[SqlFunction(
DataAccess = DataAccessKind.Read,
FillRowMethodName = "Test_FillRow",
TableDefinition = "CustId int" +
"Name nvarchar(50)")]
public static IEnumerable Test()
// This function contains the actual logic.
{
ArrayList results = new ArrayList();
using (SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
using (SqlCommand select = new SqlCommand(
"SELECT TOP 100 custid, name FROM Customers",
connection))
{
using (SqlDataReader reader = select.ExecuteReader())
{
while (reader.Read())
{
results.Add(new ResultRow(
reader.GetSqlInt32(0), // CustId
reader.GetSqlString(1) // Name
));
}
}
}
}
return results;
}
public static void Test_FillRow(
object resultsObj,
out SqlInt32 custid,
out SqlString name)
// This function takes a row and tells SQL Server what variables we want to
// return from it and what types it contains.
{
ResultRow selectResults = (ResultRow)resultsObj;
custid = selectResults.CustId;
name = selectResults.Name;
}
}
问题是,它相当重复。首先在SqlFunction块中定义表。然后,当你在返回的结果中添加或删除列时,你必须确保更新它和
- ResultRow中的定义
- ResultRow中构造函数的参数
- ResultRow中的赋值
- 在Test()中从读取器获取的类型
- Test_FillRow()中的out参数
- Test_FillRow()中的赋值
- 以及SQL查询本身,这是您开始真正考虑的唯一部分
我正在研究这样一个函数,它包含20多个参数,返回更多的行,并包含这八个可能出错的地方八个。所有的错误都是琐碎的,很容易纠正,但很容易犯,因为代码中有很多地方我必须手动保持同步。
这违反了DRY,但我不知道如何消除重复。有没有更简洁的方法来编写CLR表值函数?
如果将ResultRow替换为object[],则可以使用reader。GetValues(object[]),并且在FillRow()之前不必知道行中有什么,然后FillRow负责知道字段在原始查询中的顺序。
这真的是一种权衡,你可以放弃一直进行强输入,以换取不必一直进行强键入,但很难做到两者兼得:-)
如果将参数集划分为类,则可以使用受基类约束的泛型方法来实现数据访问。我发现,通常情况下,像这样用于分组传递参数的类在重构时通常会在其他地方找到有用的方法。
通常,将二十个参数传递给一个函数从来都不是一个好主意。
以下是一个很好的总结,我通常同意函数参数:http://benbiddington.wordpress.com/2009/06/22/book-review-of-clean-code/
具体而言:
如果一个函数需要两个或三个以上的参数,则很可能至少其中一些应该封装在自己的类中。
在这种情况下,这样做的好处可能是提取一些通用方法,帮助您更紧密地遵守DRY。