最近我发现自己正在编写数据访问层选择方法,其中代码都采用这种通用形式:
public static DataTable GetSomeData( ... arguments)
{
string sql = " ... sql string here: often it's just a stored procedure name ... ";
DataTable result = new DataTable();
// GetOpenConnection() is a private method in the class:
// it manages the connection string and returns an open and ready connection
using (SqlConnection cn = GetOpenConnection())
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
// could be any number of parameters, each with a different type
cmd.Parameters.Add("@Param1", SqlDbType.VarChar, 50).Value = param1; //argument passed to function
using (SqlDataReader rdr = cmd.ExecuteReader())
{
result.Load(rdr);
}
}
return result;
}
或者像这样:
public static DataRow GetSomeSingleRecord( ... arguments)
{
string sql = " ... sql string here: often it's just a stored procedure name ... ";
DataTable dt = new DataTable();
// GetOpenConnection() is a private method in the class:
// it manages the connection string and returns an open and ready connection
using (SqlConnection cn = GetOpenConnection())
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
// could be any number of parameters, each with a different type
cmd.Parameters.Add("@Param1", SqlDbType.VarChar, 50).Value = param1; //argument passed to function
using (SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
dt.Load(rdr);
}
}
if (dt.Rows.Count > 0)
return dt.Rows[0];
return null;
}
这些方法将由业务层代码调用,然后将基础DataTable或DataRecord转换为表示层可以使用的强类型业务对象。
由于我反复使用类似的代码,我想确保这段代码是最好的。那又怎么改进呢?并且,是否值得尝试将公共代码从此移动到它自己的方法。如果是这样,那个方法会是什么样子(特别是关于传递SqlParameter集合)?
答案 0 :(得分:3)
不得不加我自己的:
Return DataReader from DataLayer in Using statement
新模式使我一次只能在内存中有一条记录,但仍然在一个很好的'using'语句中包含连接:
public IEnumerable<T> GetSomeData(string filter, Func<IDataRecord, T> factory)
{
string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter";
using (SqlConnection cn = new SqlConnection(GetConnectionString()))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter;
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return factory(rdr);
}
rdr.Close();
}
}
}
答案 1 :(得分:2)
就客户端代码而言,我喜欢的一种模式就是这样:
DataTable data = null;
using (StoredProcedure proc = new StoredProcedure("MyProcName","[Connection]"))
{
proc.AddParameter("@LoginName", loginName);
data = proc.ExecuteDataTable();
}
我通常将连接设置为可选,并且我将以从ConnectionStrings配置部分拉出它的方式进行编码或将其视为实际的连接字符串。这让我可以在一个场景中重复使用dal,并且在使用对象构造属性存储连接字符串的COM +天中部分是habbit。
我喜欢这个,因为它很容易阅读并隐藏了我的所有ADO代码。
答案 2 :(得分:1)
我唯一不同的做法是从我自己的内部数据库助手方法切换到实际的数据访问应用程序块http://msdn.microsoft.com/en-us/library/cc309504.aspx
让知道企业库的其他开发人员更加标准化/统一,以提升代码。
答案 3 :(得分:1)
有很多方法可以实现DBAL,在我看来,你是在正确的道路上。在您的实施中需要考虑的事项:
使用DbUtil.AddParameter(cmd, "@Id", SqlDbType.UniqueIdentifier, Id);
internal class DbUtil {
internal static SqlParameter CreateSqlParameter(
string parameterName,
SqlDbType dbType,
ParameterDirection direction,
object value
) {
SqlParameter parameter = new SqlParameter(parameterName, dbType);
if (value == null) {
value = DBNull.Value;
}
parameter.Value = value;
parameter.Direction = direction;
return parameter;
}
internal static SqlParameter AddParameter(
SqlCommand sqlCommand,
string parameterName,
SqlDbType dbType
) {
return AddParameter(sqlCommand, parameterName, dbType, null);
}
internal static SqlParameter AddParameter(
SqlCommand sqlCommand,
string parameterName,
SqlDbType dbType,
object value
) {
return AddParameter(sqlCommand, parameterName, dbType, ParameterDirection.Input, value);
}
internal static SqlParameter AddParameter(
SqlCommand sqlCommand,
string parameterName,
SqlDbType dbType,
ParameterDirection direction,
object value
) {
SqlParameter parameter = CreateSqlParameter(parameterName, dbType, direction, value);
sqlCommand.Parameters.Add(parameter);
return parameter;
}
}
答案 4 :(得分:1)
首先,我认为你已经考虑过使用ORM而不是自己动手。我不会进入这个。
我对滚动您自己的数据访问代码的想法:
我的建议(我尝试了两种方法 - 建议是我提出的最新工作方法 - 它随着时间的推移而发展)。
目标是最终使用如下:
List<MyObject> objects = MyObject.FindMyObject(string someParam);
对我来说,好处是我只需更改一个文件就可以应对数据库列名,类型等的变化(一般情况下变化很小)。通过一些深思熟虑的区域,您可以组织代码,以便它们在同一对象中是独立的“层”:)。另一个好处是基类实际上可以从一个项目重用到另一个项目。并且代码膨胀是最小的(好吧,与好处相比。您还可以填充数据集并将它们绑定到UI控件:D
限制 - 每个域对象最终会有一个类(通常是每个主数据库表)。并且您无法在现有事务中加载对象(尽管您可以考虑传递事务,如果有的话)。
如果您对更多细节感兴趣,请告诉我 - 我可以稍微扩展一下答案。
答案 5 :(得分:1)
与我发布的here
类似public IEnumerable<S> Get<S>(string query, Action<IDbCommand> parameterizer,
Func<IDataRecord, S> selector)
{
using (var conn = new T()) //your connection object
{
using (var cmd = conn.CreateCommand())
{
if (parameterizer != null)
parameterizer(cmd);
cmd.CommandText = query;
cmd.Connection.ConnectionString = _connectionString;
cmd.Connection.Open();
using (var r = cmd.ExecuteReader())
while (r.Read())
yield return selector(r);
}
}
}
我有这些简单的扩展方法,以方便调用:
public static void Parameterize(this IDbCommand command, string name, object value)
{
var parameter = command.CreateParameter();
parameter.ParameterName = name;
parameter.Value = value;
command.Parameters.Add(parameter);
}
public static T To<T>(this IDataRecord dr, int index, T defaultValue = default(T),
Func<object, T> converter = null)
{
return dr[index].To<T>(defaultValue, converter);
}
static T To<T>(this object obj, T defaultValue, Func<object, T> converter)
{
if (obj.IsNull())
return defaultValue;
return converter == null ? (T)obj : converter(obj);
}
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null || obj == DBNull.Value;
}
现在我可以致电:
var query = Get(sql, cmd =>
{
cmd.Parameterize("saved", 1);
cmd.Parameterize("name", "abel");
}, r => new User(r.To<int>(0), r.To<string>(1), r.To<DateTime?>(2), r.To<bool>(3)));
foreach (var user in query)
{
}
这是完全通用的,适合任何符合ado.net界面的型号。 The connection object and reader is disposed only after the collection is enumerated once.
答案 6 :(得分:-1)
最简单的解决方案:
var dt=new DataTable();
dt.Load(myDataReader);
list<DataRow> dr=dt.AsEnumerable().ToList();