C#函数返回通用对象/实体

时间:2009-11-27 10:18:52

标签: c#

在我的应用程序中,我需要显示不同存储过程返回的记录列表。每个存储过程都返回不同类型的记录(即列数和列类型不同)。

我最初的想法是为每种类型的记录创建一个类,并创建一个函数,该函数将执行相应的存储过程并返回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',我可以绑定到图形控件,我不想创建自定义类,因为它们实际上不会被使用。什么是最好的方法?

3 个答案:

答案 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)

如果您使用匿名类,则仍在定义每种类型。您只需使用类型推断来减少键入量,类类型不会混淆任何名称空间。

在这里使用它们的缺点是你想要将它们退出方法。正如您所指出的,唯一的方法是将它们转换为对象,这样就可以在没有反射的情况下使所有数据无法访问!您是否永远不会使用此数据进行任何处理或计算?将其中一列显示为图片而不是值,或根据某些条件隐藏它?即使您只有一个条件语句,使用匿名类版本进行操作也会让您希望自己没有。