【问题标题】:Generic function for reading data from SQL Server从 SQL Server 读取数据的通用函数
【发布时间】:2013-02-15 23:02:21
【问题描述】:

假设我有两个看起来像这样的类。

class Foo1
{
   public String P1 { get; set; }
   public String P2 { get; set; }
}

class Foo2
{
   public String P3 { get; set; }
   public String P4 { get; set; }
}

P1、P2、P3 和 P4 没有任何关系。与 Foo1Foo2 相同。它们代表完全不同的对象。

我有两个数据库表,其中包含 Foo1 和 Foo2 对象的列表。我必须创建一个List<Foo1>List<Foo2>

返回 Foo1 和 Foo2 的存储过程的输入是不同的。

这是我的问题 - 我想创建一个通用函数,它可以连接到数据库并根据需要获取 Foo1 或 Foo2 列表

一开始我也想过这样的事情

internal static SqlDataReader GetData(String Proc,NameValueCollection InputParams,SqlConnection conn, ref String Err)
{

    using (SqlCommand cmd = conn.CreateCommand())
    {
        try
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = Proc;
            conn.Open();

            SqlCommandBuilder.DeriveParameters(cmd);


            foreach (SqlParameter p in cmd.Parameters)
            {
                if (p.Direction == ParameterDirection.Input)
                {

                    if (InputParams[p.ParameterName.ToLower().Substring(1)] != String.Empty)
                    {
                        p.Value = InputParams[p.ParameterName.ToLower().Substring(1)];
                        p.DbType = DbType.AnsiString;
                    }
                    else
                    {
                        Err += "<argumentnotfound>" + p.ParameterName + "</argumentnotfound>";                                    
                    }
                }
                else p.Value = DBNull.Value;

            }
            return cmd.ExecuteReader();
        }
        catch (Exception e)
        {
            return null;
        }       
    }   
} 

我不喜欢在此函数之外返回 SqlDataReader 或创建和关闭 SqlConnection 的想法。

理想情况下,我想在此函数中遍历“阅读器”对象,但返回List&lt;Foo1&gt;List&lt;Foo2&gt;。我能想到的一个解决方案是这个

        internal static List<Object> GetData(NameValueCollection InputParams, ref String Err, String ClassName, String Proc)
{
    List<Object> [] objectlist = new List<Object>();
    using (SqlConnection conn = getConnection())
    {
        using (SqlCommand cmd = conn.CreateCommand())
        {
            try
            {
                /*
                    ~~ Same as before
                */
                SqlDataReader reader =  cmd.ExecuteReader();
                while(reader.Read())
                {

                    if(ClassName == "Foo1")
                    {
                        Foo1 f = new Foo1();
                        f.P1 = reader["p1"].ToString();
                        objectList.Add(f1);
                    }
                    else if(ClassName == "Foo2")
                    {
                        /* Create and initialize Foo2 object*/
                        objectList.Add(f2);
                    }


                }
                return objectList;
            }
            catch (Exception e)
            {

            }
            finally
            {
                conn.Close();
            }
        }
    }

    return null;
}

有更好/优雅的方法吗?

编辑

我忘了提及的是——我的最终目标是为来自 Javascript 的 AJAX 调用创建 JSON。我的计划是遍历 Foo1 和 Foo2 对象列表来为每个请求创建 Json 数据。

我认为我可以在存储过程中创建 JSON 并将其作为 varchar(max) 返回并使用 JSON2.js 在客户端创建 JSOn 对象。 我在这里看到的缺点是 varchar 的 8000 字符限制。

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    我认为您不会购买太多试图将其通用化。在创建Foo1Foo2 的过程之间真正共同的部分是创建/关闭ConnectionDataReader。它们将有不同的 SQL 命令以及 DataReader 和类之间的不同映射。

    我的建议是要么使用已经抽象出数据库垃圾的 NHibernate 或实体框架之类的库,要么抽象出这些部分并拥有从 DataReader 创建 Foo1Foo2 实例的工厂。否则,您最终会得到一堆开关,这些开关会根据您尝试创建的对象类型(这是您要走的路)或使用反射自动将属性映射到数据库列来更改代码。

    【讨论】:

    • +1 - 如果像 NHibernate 或 EF 这样成熟的 ORM 太多,请查看 Dapper code.google.com/p/dapper-dot-net 或 Simple.Data - simplefx.org/simpledata/docs 之类的东西
    • @Dstanley & RussCam 你们是对的。 GetData 中的 switch 语句不是我最初打算做的事情。
    • 我已经编辑了我的问题以包括我这样做的总体目标
    【解决方案2】:

    不幸的是,我不是专家,但我想我明白你想要做什么。我的建议(尽管它可能不是最好的)是创建一个数据类型的枚举,然后在通用函数中搜索它以查找提交,然后您可以将表名用于您输入的字符串。

    我使用类似的方法来存储这种性质的信息,看起来像这样......

    功能:

        public UInt32 getKeyCode(string Key)
        {
            Constants.KeyboardControls keynum;
            if (Enum.TryParse<Constants.KeyboardControls>(Key, true, out keynum))
            {
                return (UInt32)(keynum);
            }
            else
            {
                return new UInt32();
            }
        }
    

    枚举:

        public enum KeyboardControls : uint
        {
            F1 = 0x70,
            F2 = 0x71,
            F3 = 0x72,
            F4 = 0x73,
            F5 = 0x74,
            F6 = 0x75,
            F7 = 0x76,
            F8 = 0x77,
            F9 = 0x78,
            F10 = 0x79,
            F11 = 0x7A,
            F12 = 0x7B,
            left = 0x25,
            up = 0x26,
            right = 0x27,
            down = 0x28
        }
    

    我希望这至少有一点帮助,你可以在一个枚举中列出任何对象,只需将它所说的 uint 更改为你的对象类型,左侧几乎是字符串。

    【讨论】:

    • 您能否再解释一下。我没有完全理解你的意思。
    • 我没有花时间完全阅读你的代码,如果我阅读了也不会很好理解(分析需要一个多小时),但听起来像你' 只是在泛型函数之后,如果是这种情况,对数据类型使用枚举可能很有用。比如你可以把这4种数据存储在一个枚举中,然后在调用函数的时候传入一个字符串,对这个字符串做Enum.TryParse(),看能不能在枚举中找到,然后你可以有一个对象而不是一个字符串,它可以用在一些更重要的方法中。
    猜你喜欢
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多