在我的应用程序中,我需要显示由不同存储过程返回的记录列表.每个存储过程返回不同类型的记录(即列数和列类型不同).
我最初的想法是为每种类型的记录创建一个类,并创建一个函数,该函数将执行相应的存储过程并返回List <MyCustomClass>.像这样的东西:
public class MyCustomClass1
{
public int Col1 { get; set; } //In reality the columns are NOT called Col1 and Col1 but have proper names
public int Col2 { get; set; }
}
public static List<MyCustomClass1> GetDataForReport1(int Param1)
{
List<MyCustomClass1> output = new List<MyCustomClass1>();
using (SqlConnection cn = new SqlConnection("MyConnectionString"))
using (SqlCommand cmd = new SqlCommand("MyProcNameForReport1", cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Param1", SqlDbType.Int).Value = Param1;
SqlDataReader rdr=cmd.ExecuteReader();
int Col1_Ordinal = rdr.GetOrdinal("Col1");
int Col2_Ordinal = rdr.GetOrdinal("Col2");
while (rdr.Read())
{
output.Add(new MyCustomClass1
{
Col1 = rdr.GetSqlInt32(Col1_Ordinal).Value,
Col2 = rdr.GetSqlInt32(Col2_Ordinal).Value
});
}
rdr.Close();
}
return output;
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,但因为我不需要在我的客户端代码中操作这些记录(我只需要将它们绑定到我的应用程序层中的图形控件),这样做是没有意义的,因为我会这样做有很多我不会真正使用的自定义类.我找到了这个诀窍:
public static DataTable GetDataForReport1(int Param1)
{
DataTable output = new DataTable();
using (SqlConnection cn = new SqlConnection("MyConnectionString"))
using (SqlCommand cmd = new SqlCommand("MyProcNameForReport1", cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Param1", SqlDbType.Int).Value = Param1;
output.Load(cmd.ExecuteReader());
}
return output;
}
Run Code Online (Sandbox Code Playgroud)
这将返回一个DataTable,我可以将其绑定到我在应用程序层中使用的任何控件.我想知道是否真的需要使用DataTable.
我无法返回使用匿名类创建的对象列表:
public static List<object> GetDataForReport1(int Param1)
{
List<object> output = new List<object>();
using (SqlConnection cn = new SqlConnection("MyConnectionString"))
using (SqlCommand cmd = new SqlCommand("MyProcNameForReport1", cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Param1", SqlDbType.Int).Value = Param1;
SqlDataReader rdr=cmd.ExecuteReader();
int Col1_Ordinal = rdr.GetOrdinal("Col1");
int Col2_Ordinal = rdr.GetOrdinal("Col2");
while (rdr.Read())
{
output.Add(new
{
Col1 = rdr.GetSqlInt32(Col1_Ordinal).Value,
Col2 = rdr.GetSqlInt32(Col2_Ordinal).Value
});
}
rdr.Close();
}
return output;
}
Run Code Online (Sandbox Code Playgroud)
还有其他想法吗?基本上我只是希望函数返回'something',我可以绑定到图形控件,我不想创建自定义类,因为它们实际上不会被使用.什么是最好的方法?
好消息是:这并不是第一次出现实体与数据集问题。这是一个比我自己的编程经验更古老的争论。如果您不想编写 DTO 或自定义实体,那么您的选择是 DataTables/DataSets,或者您可以再次重新发明这个轮子。你不会是第一个,也不会是最后一个。DTO/实体的论点是,您没有数据集带来的性能开销。数据集必须存储有关各个列的数据类型等的大量额外信息......
您可能会很高兴听到的一件事是,如果您选择自定义实体路由并且您很高兴您的对象属性名称与存储过程返回的列名称相匹配,那么您可以跳过编写所有那些 GetDataForReport() 映射函数,您可以在其中编写所有这些 GetDataForReport() 映射函数。使用 GetOrdinal 将列映射到属性。幸运的是,一些聪明的猴子在这里很好地解决了这个问题。
编辑:我今天正在研究一个完全不同的问题(将数据集绑定到 silverlight datagrids),并看到了Vladimir Bodurov 的这篇文章,其中展示了如何将 IDictionary 的 IEnumerable 转换为动态创建对象的 IEnumerable (使用 IL)。我想到您可以轻松修改扩展方法以接受 datareader 而不是 IDictionary 的 IEnumerable 来解决动态集合的问题。它太酷了。我认为它会完全实现您所追求的目标,因为您不再需要数据集或自定义实体。实际上,您最终得到了一个自定义实体集合,但您失去了编写实际类的开销。
如果你很懒,这里有一个将数据读取器转换为弗拉基米尔字典集合的方法(它比实际转换他的扩展方法效率低):
public static IEnumerable<IDictionary> ToEnumerableDictionary(this IDataReader dataReader)
{
var list = new List<Dictionary<string, object>>();
Dictionary<int, string> keys = null;
while (dataReader.Read())
{
if(keys == null)
{
keys = new Dictionary<int, string>();
for (var i = 0; i < dataReader.FieldCount; i++)
keys.Add(i, dataReader.GetName(i));
}
var dictionary = keys.ToDictionary(ordinalKey => ordinalKey.Value, ordinalKey => dataReader[ordinalKey.Key]);
list.Add(dictionary);
}
return list.ToArray();
}
Run Code Online (Sandbox Code Playgroud)