Как передать в метод список неизвестных объектов типа custom-class, содержащих некоторые свойства? - PullRequest
1 голос
/ 03 марта 2011

Я делаю класс databasehelper с методами для доступа к базе данных SQLCE.Я хочу использовать один и тот же метод для чтения строк, используя разные классы, содержащие свойства, которые соответствуют полям в разных таблицах.Используемый класс определяется во время выполнения, и я хочу передать в список метод с объектами из класса, получить имена свойств и использовать их для чтения базы данных.Было бы очень удобно, потому что я мог бы использовать его для всех моих (SQLCE-) баз данных.

(я обновил ошибочный код, чтобы предоставить решение здесь)

    #region ReadData
    ///----------------------------------------------------------------------
    /// <summary>
    /// Reads datarows from database and adds them to list.
    /// </summary>
    /// <param name="data">List containing objects with properties.</param>
    /// <param name="table">Table in database.</param>
    /// <param name="search">Substring of SQL-statement that follows 'WHERE'.</param>
    /// <param name="connect">Connectionstring.</param>
    /// <returns>true if successfull</returns>
    ///----------------------------------------------------------------------
    public static bool ReadData<T>(List<T> data, string table, string search, string connect) where T : class, new()
    {
        // Return if input id missing
        if (data == null || table == "" || connect == "") return false;

        // retrieve properties from Data 
        PropertyInfo[] propinf = typeof(T).GetProperties();

        // Create string with SQL-statement
        string fields = "";
        // retrieve fields from propinf
        foreach (PropertyInfo p in propinf)
        {
            fields += fields == "" ? p.Name : ", " + p.Name;
        }
        // create SQL SELECT statement with properties and search
        string sql = "SELECT " + fields + " FROM " + table;
        sql += search == "" ? "" : " WHERE " + search;

        // Instantiate and open database
        SqlCeConnection cn = new SqlCeConnection(connect);
        if (cn.State == ConnectionState.Closed)
        cn.Open();
        data.Clear();   // just in case
        try
        {
            SqlCeCommand cmd = new SqlCeCommand(sql, cn);
            cmd.CommandType = CommandType.Text;
            SqlCeResultSet rs = cmd.ExecuteResultSet(ResultSetOptions.Scrollable);
            if (rs.HasRows)  // Only if database is not empty
            {
                while (rs.Read()) // read database
                {
                    // instantiate single item of list Data
                    var dataitem = new T();
                    int ordinal = 0;
                    foreach (PropertyInfo p in propinf)
                    {
                        // read database and
                        PropertyInfo singlepropinf = typeof(T).GetProperty(p.Name);
                        ordinal = rs.GetOrdinal(p.Name);
                        singlepropinf.SetValue(dataitem, rs.GetValue(ordinal), null); // fill data item
                    }
                    data.Add(dataitem);  // and add it to data.
                }
            }
            else
            {
                MessageBox.Show("No records matching '" + search + "'!");
                return false;
            }
        }
        catch (SqlCeException sqlexception)
        {
            MessageBox.Show(sqlexception.Message, "SQL-error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return false;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return false;
        }
        finally
        {
            cn.Close();
        }
        return true;
    }
    #endregion

У меня было двавопросы:

1) как передать этот список с неизвестным типом?Ответы, которые я нашел до сих пор, не помогли мне решить эту проблему.

2) как создать экземпляр объекта класса неизвестного типа (во время компиляции), чтобы добавить его в список, не вызывая компиляцию-ошибка?

Большое спасибо!

Ответы [ 2 ]

5 голосов
/ 03 марта 2011

1: список неизвестного типа может быть неуниверсальным IList, или ArrayList, или List<object>

2: Activator.CreateInstance(type)

В качестве альтернативы, посмотрите нанаписать общий метод, в идеале что-то вроде:

ReadData<T>(List<T> data, ...) where T : class, new()

и использовать new T() для создания новых предметов, и typeof(T), чтобы говорить о Type.При использовании универсального метода вызывающая сторона предоставляет T - часто неявно.Обратите внимание, что в вашем примере ref не требуется.

0 голосов
/ 07 марта 2011

Ниже приведено обновление кода. Он приближается к финалу и был протестирован в самых разных ситуациях. В идеале итерации, использующие рефлексию, должны быть заменены чем-то менее интенсивным по производительности, но пока операции с базой данных отнимают много времени, я думаю, это не имеет большого значения в реальной жизни. Я уже вполне доволен этим.

    #region Read(Like)Data
    public static int ReadData<T>(List<T> data, string table, T search, string connect) where T : class, new()
    {
        return BaseRead(data, table, search, connect, "=");
    }
    public static int ReadLikeData<T>(List<T> data, string table, T search, string connect) where T : class, new()
    {
        return BaseRead(data, table, search, connect, "LIKE");
    }
    ///----------------------------------------------------------------------
    /// <summary>
    /// Reads datarows from database and adds them to list containing objects of type T.
    /// Note that the properties of T should match the fields of the database table.
    /// </summary>
    /// <param name="data">List containing objects of type T with properties matching fields in table.</param>
    /// <param name="table">Table in database.</param>
    /// <param name="search">Object of type T with (some) properties containing search constraints, 
    /// others should be null. Unused DateTime should be 1800-01-01.</param>
    /// <param name="connect">Connectionstring.</param>
    /// <returns>-1 if exception was thrown or the number of records (objects of type T) otherwise</returns>
    ///----------------------------------------------------------------------
    private static int BaseRead<T>(List<T> data, string table, T search, string connect, string comparer) where T : class, new()
    {
        // Abort if insufficient arguments
        if (data == null || table == "" || connect == "") return 0;
        // Make sure List<T> data is empty
        data.Clear();
        // Retrieve properties from object of type T 
        PropertyInfo[] propinfs = typeof(T).GetProperties();

        // -----------------------------------------
        // Create string that contains SQL-statement
        // -----------------------------------------
        string fields = ""; string wherestr = "";
        // Retrieve fields from propinf
        foreach (PropertyInfo p in propinfs)
        {
            fields += fields == "" ? p.Name : ", " + p.Name;
            dynamic propvalue = p.GetValue(search, null);
            // Solutions for properties of type DateTime
            long dateticks = 0; DateTime dt = new DateTime();
            Type type = propvalue != null ? propvalue.GetType() : null;
            if (propvalue != null && propvalue.GetType() == dt.GetType())
            {
                dt = propvalue;
                dateticks = dt.Ticks;
            }
            // DateTime 1800-01-01 equals null (hey, it's better than nothing...)
            if (propvalue != null && dt != DateTimeNull)
                wherestr += wherestr == "" ? p.Name + " " + comparer + " @" + p.Name.ToLower() 
                    : " AND " + p.Name + " " + comparer + " @" + p.Name.ToLower();
        }
        // Create SQL SELECT statement with properties and search
        string sql = "SELECT " + fields + " FROM " + table;
        sql += wherestr == "" ? "" : " WHERE " + wherestr;

        // -------------------
        // Database operations
        // -------------------
        SqlCeConnection cn = new SqlCeConnection(connect);
        if (cn.State == ConnectionState.Closed) cn.Open();
        try
        {
            SqlCeCommand cmd = new SqlCeCommand(sql, cn);
            cmd.CommandType = CommandType.Text;
            // Add propertyvalues to WHERE-statement using reflection
            foreach (PropertyInfo p in propinfs)
            {
                dynamic propvalue = p.GetValue(search, null);
                // Except for DateTime values 1800-01-01 (defined as null)
                if (propvalue != null && !(propvalue.GetType() is DateTime && propvalue != DateTimeNull))
                {
                    if (comparer == "LIKE") propvalue = "%" + propvalue + "%";
                    cmd.Parameters.AddWithValue("@" + p.Name.ToLower(), propvalue);
                }
            }
            SqlCeResultSet rs = cmd.ExecuteResultSet(ResultSetOptions.Scrollable);
            if (rs.HasRows)  // Only if database is not empty
            {
                while (rs.Read()) // Read next row in database
                {
                    // Instantiate single item of List data
                    var dataitem = new T();  // Object to put the field-values in
                    foreach (PropertyInfo p in propinfs)
                    {
                        // Read database fields using reflection
                        PropertyInfo singlepropinf = typeof(T).GetProperty(p.Name);
                        int ordinal = rs.GetOrdinal(p.Name);
                        dynamic result = rs.GetValue(ordinal);
                        // Conversion to null in case field is DBNull
                        if (result is DBNull)
                        {
                            if (singlepropinf.PropertyType.Equals(typeof(DateTime)))
                            {
                                singlepropinf.SetValue(dataitem, DateTimeNull, null); // Fill data item with datetimenull
                            }
                            else
                            {
                                singlepropinf.SetValue(dataitem, null, null); // Fill data item with null
                            }
                        }
                        else
                        {
                            singlepropinf.SetValue(dataitem, result, null); // Or fill data item with value
                        }
                    }
                    data.Add(dataitem);  // And add the record to List<T> data.
                }
            }
            else
            {
                //MessageBox.Show("No records matching '" + wherestr + "'!");
                return 0;
            }
        }
        catch (SqlCeException sqlexception)
        {
            MessageBox.Show(sqlexception.Message, "SQL-error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return -1;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return -1;
        }
        finally
        {
            cn.Close();
        }
        // Return number of objects (should equal number of retrieved records)
        return data.Count();
    }
    #endregion
...