在我的应用程序中,我需要显示不同存储过程返回的记录列表。每个存储过程都返回不同类型的记录(即列数和列类型不同)。
我最初的想法是为每种类型的记录创建一个类,并创建一个函数,该函数将执行相应的存储过程并返回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;
}
这很好但是因为我不需要在我的客户端代码中操作那些记录(我只需要将它们绑定到我的应用程序层中的图形控件),这样做是没有意义的。我最终会有很多我不会实际使用的自定义类。我找到了这个诀窍:
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;
}
这将返回一个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;
}
还有其他想法吗?基本上我只是希望函数返回'something',我可以绑定到图形控件,我不想创建自定义类,因为它们实际上不会被使用。什么是最好的方法?
答案 0 :(得分:4)
好消息是:这不是entities vs datasets问题第一次出现。这是一场比我自己的编程经历更早的辩论。如果您不想编写DTO或自定义实体,那么您的选项是DataTables / DataSet,或者您可以再次重新发明此轮。你不会是第一个,你不会是最后一个。 DTO / Entities的论点是您没有使用DataSet获得的性能开销。 DataSet必须存储大量有关各列数据类型等的额外信息......
有一件事,你可能会高兴地听到,如果你去自定义实体路由并且你很高兴你的对象属性名称与你的sprocs返回的列名相匹配,你可以跳过编写所有这些GetDataForReport()映射您正在使用GetOrdinal将列映射到属性的函数。幸运的是,一些聪明的猴子正确地怀疑这个问题here。
编辑:我今天正在研究一个完全不同的问题(将数据集绑定到silverlight数据网格),弗拉基米尔·博杜罗夫(Vladimir Bodurov)遇到this article,它展示了如何将IEnumerable的IDictionary转换为动态创建的IEnumerable对象(使用IL)。我发现你可以轻松修改扩展方法以接受datareader而不是IDictionary的IEnumerable来解决你的动态集合问题。它太酷了。我认为它将完全符合您的要求,因为您不再需要数据集或自定义实体。实际上,您最终会得到一个自定义实体集合,但是您将失去编写实际类的开销。如果你很懒,这里有一个方法可以将datareader变成Vladimir的字典集合(它的效率低于实际转换他的扩展方法):
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();
}
答案 1 :(得分:1)
如果您要对它们进行XmlSerialize并希望它们有效,那么它们就不能是匿名的。你要发回这些变化吗?匿名类不会有太多用处。
如果你真的不想对除了网格化之外的数据做任何事情,那么DataTable可能是你上下文的一个很好的答案。
一般来说,如果有任何有趣的商业登录,你应该使用数据传输对象等,而不是仅仅在网格上运送。
答案 2 :(得分:0)
如果您使用匿名类,则仍在定义每种类型。您只需使用类型推断来减少键入量,类类型不会混淆任何名称空间。
在这里使用它们的缺点是你想要将它们退出方法。正如您所指出的,唯一的方法是将它们转换为对象,这样就可以在没有反射的情况下使所有数据无法访问!您是否永远不会使用此数据进行任何处理或计算?将其中一列显示为图片而不是值,或根据某些条件隐藏它?即使您只有一个条件语句,使用匿名类版本进行操作也会让您希望自己没有。