2012-12-31 31 views
6

Tôi có một số lớp đại diện cho các bảng cơ sở dữ liệu, để tải các hàng của mỗi bảng trên DataGridView, tôi có một hàm List<> bên trong một vòng lặp nhận tất cả các hàng từ bảng đó.Làm thế nào để tự động chỉ định loại danh sách <> chức năng?

public List<class_Table1> list_rows_table1() 
{ 
    // class_Table1 contains each column of table as public property 
    List<class_Table1> myList = new List<class_Table1>(); 

    // sp_List_Rows: stored procedure that lists data 
    // from Table1 with some conditions or filters 
    Connection cnx = new Connection; 
    Command cmd = new Command(sp_List_Rows, cnx); 

    cnx.Open; 
    IDataReader dr = cmd.ExecuteReader(); 

    while (dr.Read()) 
    { 
     class_Table1 ct = new class_Table1(); 

     ct.ID = Convert.ToInt32(dr[ID_table1]); 
     ct.Name = dr[name_table1].ToString(); 
     //... all others wanted columns follow here 

     myList.Add(ct); 
    } 
    dr.Close(); 
    cnx.Close(); 

    // myList contains all wanted rows; from a Form fills a dataGridView 
    return myList(); 
} 

Và đối với các bảng khác, một số chức năng khác: list_rows_table2, list_rows_table3 ... Câu hỏi của tôi là: Làm thế nào để tạo ra một chỉ List<> chức năng, nơi tôi có thể tự động xác định loại List<> trả lại, hoặc làm thế nào để chuyển đổi, ví dụ: List<object> thành List<myClass> trước khi quay lại.

+3

Điều này về cơ bản là ORM. Tại sao không sử dụng ORM? Entity Framework hoạt động khá tốt, chúng tôi đang sử dụng nó trong một ứng dụng LOB lớn với hơn 400 khách hàng đang chạy một ứng dụng SAAS (với 3 máy tính avg mỗi máy) và phía máy chủ được lưu trữ trong các máy chủ của chúng tôi. –

+2

Hãy xem một [ValueInjecter] (http://valueinjecter.codeplex.com/) và đặc biệt để [ví dụ này] (http://goo.gl/mD5OG), điều này ánh xạ một trình đọc dữ liệu vào một Danh sách các đối tượng miền theo cách bạn muốn làm. Regardas và chúc mừng năm mới! – Hugo

Trả lời

1

Việc triển khai của Olivier là tốt. Nó sử dụng generics và giao diện cho mỗi thực thể nó thực hiện riêng của FillFromDataReader().

Bạn có thể mang nó xa hơn. Bằng cách sử dụng quy ước tất cả các mã hydrat hóa dữ liệu có thể được tập trung và trừu tượng đi.

Tôi sẽ giả định rằng tên thuộc tính lớp và tên cột của bạn giống nhau. Nếu không thì mã sau có thể được mở rộng để thêm các thuộc tính bí danh vào các tên thuộc tính. Đôi khi một thuộc tính được tính từ các giá trị khác trong đối tượng, thuộc tính này không thể được ngậm nước. Thuộc tính Ignore có thể được tạo và thực hiện trong lớp bên dưới.

public class DataAccess 
{ 
    /// <summary> 
    /// Hydrates the collection of the type passes in. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="sql">The SQL.</param> 
    /// <param name="connection">The connection.</param> 
    /// <returns>List{``0}.</returns> 
    public List<T> List<T>(string sql, string connection) where T: new() 
    { 
     List<T> items = new List<T>(); 

     using (SqlCommand command = new SqlCommand(sql, new SqlConnection(connection))) 
     { 
      string[] columns = GetColumnsNames<T>(); 
      var reader = command.ExecuteReader(CommandBehavior.CloseConnection); 

      while (reader.Read()) 
      { 
       T item = new T(); 

       foreach (var column in columns) 
       { 
        object val = reader.GetValue(reader.GetOrdinal(column)); 
        SetValue(item, val, column); 
       } 

       items.Add(item); 
      } 

      command.Connection.Close(); 

     } 

     return items; 
    } 

    /// <summary> 
    /// Sets the value. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="item">The item.</param> 
    /// <param name="value">The value.</param> 
    /// <param name="column">The column.</param> 
    private void SetValue<T>(T item, object value, string column) 
    { 
     var property = item.GetType().GetProperty(column); 
     property.SetValue(item, value, null); 
    } 

    /// <summary> 
    /// Gets the columns names. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <returns>System.String[][].</returns> 
    private string[] GetColumnsNames<T>() where T : new() 
    { 
     T item = new T(); 

     return (from i in item.GetType().GetProperties() 
       select i.Name).ToArray(); 
    } 
} 

Có một vài cảnh báo trước ở mã trên. Các kiểu DBNulls và Nullable là các trường hợp đặc biệt và sẽ yêu cầu mã tùy chỉnh để xử lý chúng. Tôi thường chuyển DBNull thành null. Tôi chưa bao giờ gặp phải một trường hợp mà tôi cần phân biệt sự khác biệt giữa hai người. Đối với các kiểu Nullalbe, chỉ cần phát hiện kiểu Nullable và xử lý mã tương ứng.

Một ORM sẽ loại bỏ nhiều nguyên nhân gây đau đầu khi truy cập dữ liệu. Nhược điểm, là nhiều lần bạn được kết hợp với DTO và lược đồ cơ sở dữ liệu. Tất nhiên vấn đề này có thể được chứa bằng cách sử dụng abstractions.Nhiều công ty vẫn sử dụng các thủ tục được lưu trữ nghiêm ngặt, hầu hết các ORM rơi xuống khi họ buộc phải tiêu thụ các thủ tục được lưu trữ. Chúng không được thiết kế để làm việc với các thủ tục được lưu trữ.

Tôi đã viết khung truy cập dữ liệu có tên "Hypersonic". Nó trên GitHub, nó được thiết kế đặc biệt để làm việc với các thủ tục lưu sẵn. Đoạn mã trên là một thực thi ánh sáng của nó.

+1

[Hydration] (http://stackoverflow.com/a/4929478/880990) –

+1

Các vấn đề với mã trong câu trả lời này: 1. SQLConnection không được xử lý đúng cách trong "sử dụng" hoặc "thử/cuối cùng". 2. Tại sao nó sử dụng "reader.GetValue (reader.GetOrdinal (column))" thay vì đọc [column]? 3. SetValue đang sử dụng sự phản chiếu để đặt giá trị. Nếu bạn có một số lượng lớn các mục, điều này sẽ rất chậm. 4. GetColumnNames chỉ nên sử dụng typeof (T) thay vì mới T(). GetType(). –

7

Bạn có thể có một giao diện mà tất cả các lớp dữ liệu của bạn phải thực hiện

public interface IData 
{ 
    void FillFromReader(IDataReader dr); 
} 

Sau đó thay đổi phương pháp của bạn như thế này

public List<T> GetList<T>(string sqlText) 
    where T : IData, new() 
{ 
    List<T> myList = new List<T>(); 

    using (Connection cnx = new Connection(connString)) 
    using (Command cmd = new Command(sqlText, cnx)) { 
     cnx.Open(); 
     using (IDataReader dr = cmd.ExecuteReader()) { 
      while (dr.Read()) 
      { 
       T item = new T(); 
       item.FillFromReader(dr); 
       myList.Add(item); 
      } 
     } 
    } 
    return myList(); 
} 

Vì vậy, về cơ bản mỗi lớp sẽ có trách nhiệm làm đầy các lĩnh vực riêng của mình.

Ràng buộc where T : IData, new() cho thông số loại chung là rất quan trọng. Nó cho biết phương thức, rằng T phải triển khai giao diện IData. Điều này là cần thiết để có thể gọi phương thức FillFromReader mà không cần truyền. Các lớp dữ liệu phải có một constructor mặc định (điều này được quy định bởi new(). Điều này cho phép bạn nhanh chóng một với new T().


tôi bao quanh mã bằng cách sử dụng kết nối, lệnh và đọc dữ liệu với using báo cáo. Các using tuyên bố đóng cửa và giải phóng các nguồn lực tự động ở phần cuối của khối và đảm bảo rằng điều này xảy ra, ngay cả khi một ngoại lệ nên được ném hoặc khối lệnh nên để sớm với sự trở lại-tuyên bố ví dụ.

Xem using Statement (C# Reference)

Các vấn đề liên quan