简体   繁体   English

用于从SQL Server读取数据的通用函数

[英]Generic function for reading data from SQL Server

Lets say I have two classes which look like these. 可以说我有两个看起来像这样的课程。

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 and P4 are in no way related. P1,P2,P3和P4没有任何关系。 Same with Foo1 and Foo2 . Foo1Foo2相同。 They represent totally different objects. 它们代表完全不同的对象。

I have two database tables which have a list of Foo1 and Foo2 objects. 我有两个数据库表,其中包含Foo1和Foo2对象的列表。 I have to create a List<Foo1> and List<Foo2> 我必须创建一个List<Foo1>List<Foo2>

The input for the stored procedures which return the Foo1's and Foo2's are different. 返回Foo1和Foo2的存储过程的输入是不同的。

Here is my problem - I want to create a generic function that would connect to database and fetch either Foo1 or Foo2 list as I wish 这是我的问题 - 我想创建一个通用函数,它将连接到数据库并按照我的意愿获取Foo1或Foo2列表

Initially I thought of something like this 最初我想到了这样的事情

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;
        }       
    }   
} 

I didn't like the idea of returning SqlDataReader or creating and closing SqlConnection outside this function. 我不喜欢在这个函数之外返回SqlDataReader或创建和关闭SqlConnection的想法。

Ideally, I would like to iterate through "reader" object inside this function but return either List<Foo1> or List<Foo2> . 理想情况下,我想迭代此函数内的“reader”对象,但返回List<Foo1>List<Foo2> One solution i can think of is this 我能想到的一个解决方案就是这个

        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;
}

Is there a better/elegant approach? 有更好/更优雅的方法吗?

Edit 编辑

What I forgot to mention is this - My final objective is to create JSON for AJAX calls from Javascript. 我忘了提到的是 - 我的最终目标是从Javascript为AJAX调用创建JSON My plan was to iterate over Foo1 and Foo2 object lists to create Json data for each request. 我的计划是迭代Foo1和Foo2对象列表,为每个请求创建Json数据。

I think that I can create JSON inside the stored procedures and return it as a varchar(max) and use JSON2.js to create JSOn objects on the client side. 我认为我可以在存储过程中创建JSON并将其作为varchar(max)返回,并使用JSON2.js在客户端创建JSOn对象。 The downside I see here is the limit of 8000 char limit on the varchar. 我在这里看到的缺点是对varchar的限制为8000 char限制。

I don't think you're going to buy much trying to genericize this. 我不认为你会买太多试图将这个泛化。 The only pieces that are truly common between the procedures to create Foo1 and Foo2 are creating/closing the Connection and DataReader . 在创建Foo1Foo2的过程之间真正常见的唯一部分是创建/关闭ConnectionDataReader They are going to have different SQL commands and different mappings between the DataReader and the class. 它们将在DataReader和类之间具有不同的SQL命令和不同的映射。

My advice would be to either use a library like NHibernate or Entity Framework that already abstracts away the database grunge, or abstract away those parts and have factories that create Foo1 and Foo2 instances from a DataReader . 我的建议是使用像NHibernate这样的库或已经抽象掉数据库垃圾的实体框架,或者抽象掉这些部分并让工厂从DataReader创建Foo1Foo2实例。 Otherwise you're going to end up with a bunch of switches that change the code based on the type of object you're trying to create (which is the road you are headed down) or using reflection to automatically map properties to database columns. 否则,您将最终得到一堆开关,这些开关根据您尝试创建的对象类型(这是您要走的路)改变代码,或使用反射自动将属性映射到数据库列。

I'm not an expert unfortunately, but I think I get what you're trying to do. 不幸的是,我不是专家,但我想我得到了你想做的事。 My advise (though it may not be the best) would be to create an Enum of the data types, then search it for a submission in the generic function, then you can use the tablename against a string which you've inputted. 我的建议(虽然它可能不是最好的)是创建一个数据类型的枚举,然后在泛型函数中搜索它的提交,然后你可以对你输入的字符串使用tablename。

I use a similar method for storing information of this nature, it looks like this... 我使用类似的方法来存储这种性质的信息,它看起来像这样......

Function: 功能:

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

Enumeration: 列举:

    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
    }

I hope this helps at least a bit, you can list any object in an enum, just change where it says uint to your object type, and the left side is pretty much strings. 我希望这至少有一点帮助,你可以在枚举中列出任何对象,只需改变它对你的对象类型说uint的地方,左边几乎是字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM