2010-11-05 53 views
19

thể trùng lặp:
DataTable to Generic List (memory leak?)Chuyển đổi DataTable để Generic Danh sách trong C#

thể trùng lặp:
Convert DataTable to List<>

thể trùng lặp:
Fastest way to convert datatable to generic list

Disclaimer: Tôi biết mình hỏi ở rất nhiều nơi tại SO.
Truy vấn của tôi hơi khác một chút.

Mã hóa Ngôn ngữ: C# 3.5

Tôi có một DataTable tên cardsTable mà kéo dữ liệu từ DB và tôi có một Thẻ lớp mà chỉ có một số tài sản (không có constructor)

public class Cards 
{ 
    public Int64 CardID { get; set; } 
    public string CardName { get; set; } 
    public Int64 ProjectID { get; set; } 
    public Double CardWidth { get; set; } 
    public Double CardHeight { get; set; } 
    public string Orientation { get; set; } 
    public string BackgroundImage { get; set; } 
    public string Background { get; set; } 
} 

Tôi muốn chèn dữ liệu thẻTable vào một đối tượng của danh sách loại.
Dữ liệu của tôi sẽ có các trường rỗng trong đó và do đó phương pháp không nên lỗi khi tôi chuyển đổi dữ liệu. Phương pháp dưới đây là cách tốt nhất?

DataTable dt = GetDataFromDB(); 
List<Cards> target = dt.AsEnumerable().ToList().ConvertAll(x => new Cards { CardID = (Int64)x.ItemArray[0] }); 
+0

Câu hỏi này có bị đóng hoặc nhầm lẫn không? :) –

+0

@marc garvell: Tôi muốn thao tác dữ liệu có thể đặt trước được ... – naveen

+1

bạn có một hàm tạo ... một giá trị mặc định. –

Trả lời

29

bạn thực sự có thể rút ngắn nó xuống đáng kể. Bạn có thể nghĩ về phương thức mở rộng Select() làm trình chuyển đổi loại.Chuyển đổi có thể được viết như sau:

List<Cards> target = dt.AsEnumerable() 
    .Select(row => new Cards 
    { 
     // assuming column 0's type is Nullable<long> 
     CardID = row.Field<long?>(0).GetValueOrDefault(), 
     CardName = String.IsNullOrEmpty(row.Field<string>(1)) 
      ? "not found" 
      : row.Field<string>(1), 
    }).ToList(); 
+0

@ Jeff: Và những gì về loại cột không phải là nullable ... như CardName = row.Field (0) – naveen

+1

Tôi đã đề cập đến nó được rút ngắn đáng kể. Trên bề mặt, nó trông không khác lắm. Những gì tôi muốn nói là nó có thể được viết hiệu quả hơn. Cách tiếp cận ban đầu của bạn tạo ra hai cá thể danh sách khác nhau lặp lại chiều dài của bảng hai lần. Cách tiếp cận này thực hiện chuyển đổi trong một lần. Chỉ để bạn biết. –

+1

@naveen: các chuỗi là vô giá trị, chúng là các kiểu tham chiếu, không phải kiểu giá trị rỗng. Có điều gì đặc biệt mà bạn muốn làm với điều đó không? –

2

tốt của nó là giải pháp một dòng

nó phụ thuộc vào hay không, bạn biết dữ liệu trong cơ sở dữ liệu là tất cả hợp lệ và sẽ không chứa bất cứ điều gì mà sẽ phá vỡ trên

ví dụ một trường nullable khi bạn không mong đợi nó - có thể do một tham gia trái int eh sql mà genertates dữ liệu.

Vì vậy, nếu bạn đã xác thực dữ liệu trước đó thì vâng - Tôi đã đi để gợi ý một số LINQ - nhưng bạn đã bị hỏng.

Nếu bạn cần xác thực, bạn có thể chỉ cần lặp qua các dữ liệu, tạo đối tượng như trên và thêm nó vào bộ sưu tập ... điều này cũng sẽ cho phép bạn xử lý lỗi trong một hàng và vẫn xử lý phần còn lại.

Thats đường tôi nhìn thấy nó anyway

(chết tiệt tôi đã vào sân để downvote một cái gì đó để đại diện của tôi là 1024)

6

Các ToList() là ở vị trí sai, và nếu một số lĩnh vực có thể rỗng bạn sẽ phải đối phó với những khi họ wont chuyển đổi sang Int64 nếu họ vô

DataTable dt = GetDataFromDB(); 
List<Cards> target = dt.AsEnumerable().Select(
    x => new Cards { CardID = (Int64)(x.ItemArray[0] ?? 0) }).ToList(); 
+1

'ConvertAll()' làm phương thức 'Liệt kê '. Nó không phải là một phương thức mở rộng cũng không phải là 'IEnumerable 'xác định rõ ràng nó. Tương tự gần nhất trong 'IEnumerable ' sẽ là 'Select()'. –

+0

Bắt tốt. đã cập nhật. – Jamiec

9

Tôi nghĩ rằng tất cả các giải pháp có thể được cải thiện và làm cho phương pháp tổng quát hơn nếu bạn sử dụng một số quy ước và phản ánh. Giả sử bạn đặt tên cho các cột trong cùng một tên như các thuộc tính trong đối tượng, sau đó bạn có thể viết một cái gì đó xem xét tất cả các thuộc tính của đối tượng của bạn và sau đó tra cứu cột đó trong dữ liệu để ánh xạ giá trị.

tôi đã ngược lại, đó là ... từ IList để DataTable, và mã tôi đã viết có thể được xem tại địa chỉ: http://blog.tomasjansson.com/convert-datatable-to-generic-list-extension/

Nó không nên là khó để đi theo con đường khác, và nó nên khó có thể quá tải các chức năng để bạn có thể cung cấp thông tin về các thuộc tính bạn muốn bao gồm hoặc loại trừ.

EDIT: Vì vậy, các mã để làm cho nó làm việc là:

public static class DataTableExtensions 
{ 
    private static Dictionary<Type,IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>(); 
    public static IList<PropertyInfo> GetPropertiesForType<T>() 
    { 
     var type = typeof(T); 
     if(!typeDictionary.ContainsKey(typeof(T))) 
     { 
      typeDictionary.Add(type, type.GetProperties().ToList()); 
     } 
     return typeDictionary[type]; 
    } 

    public static IList<T> ToList<T>(this DataTable table) where T : new() 
    { 
     IList<PropertyInfo> properties = GetPropertiesForType<T>(); 
     IList<T> result = new List<T>(); 

     foreach (var row in table.Rows) 
     { 
      var item = CreateItemFromRow<T>((DataRow)row, properties); 
      result.Add(item); 
     } 

     return result; 
    } 

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new() 
    { 
     T item = new T(); 
     foreach (var property in properties) 
     { 
      property.SetValue(item, row[property.Name], null); 
     } 
     return item; 
    } 

} 

Nếu bạn có một DataTable bạn chỉ có thể viết yourTable.ToList<YourType>() và nó sẽ tạo ra danh sách cho bạn. Nếu bạn có loại phức tạp hơn với các đối tượng lồng nhau, bạn cần phải cập nhật mã. Một gợi ý là chỉ cần quá tải phương thức ToList để chấp nhận một params string[] excludeProperties chứa tất cả các thuộc tính của bạn không được ánh xạ. Tất nhiên bạn có thể thêm một số kiểm tra null trong vòng foreach của phương thức CreateItemForRow.

CẬP NHẬT: Đã thêm từ điển tĩnh để lưu trữ kết quả từ hoạt động phản chiếu để làm cho nó nhanh hơn một chút. Tôi đã không biên dịch mã, nhưng nó sẽ làm việc :).

+0

Trong 'CreateItemFromRow' bạn nên kiểm tra' if (row.Table.Columns.Contains (property.Name)) 'và sau đó phát hành' property.SetValue (.. '. Điều này sẽ đảm bảo rằng không có ngoại lệ nào được ném nếu có là tên thuộc tính không nằm trên hàng dữ liệu – user20358

3

Chỉ cần đơn giản hóa một chút. Tôi không sử dụng ItemArray:

List<Person> list = tbl.AsEnumerable().Select(x => new Person 
        { 
         Id = (Int32) (x["Id"]), 
         Name = (string) (x["Name"] ?? ""), 
         LastName = (string) (x["LastName"] ?? "") 
        }).ToList(); 
+0

Xin cảm ơn Buddy. –

0

Bạn có thể ánh xạ Bảng dữ liệu thành lớp mô hình bằng cách sử dụng lớp Chung như dưới đây.

Generic lớp

public static class DataTableMappingtoModel 
    { 
     /// <summary> 
     /// Maps Data Table values to coresponded model propertise 
     /// </summary> 
     /// <typeparam name="T"></typeparam> 
     /// <param name="dt"></param> 
     /// <returns></returns> 
     public static List<T> MappingToEntity<T>(this DataTable dt) 
     { 
      try 
      { 
       var lst = new List<T>(); 
       var tClass = typeof (T); 
       PropertyInfo[] proInModel = tClass.GetProperties(); 
       List<DataColumn> proInDataColumns = dt.Columns.Cast<DataColumn>().ToList(); 
       T cn; 
       foreach (DataRow item in dt.Rows) 
       { 
        cn = (T) Activator.CreateInstance(tClass); 
        foreach (var pc in proInModel) 
        { 


          var d = proInDataColumns.Find(c => string.Equals(c.ColumnName.ToLower().Trim(), pc.Name.ToLower().Trim(), StringComparison.CurrentCultureIgnoreCase)); 
          if (d != null) 
           pc.SetValue(cn, item[pc.Name], null); 


        } 
        lst.Add(cn); 
       } 
       return lst; 
      } 
      catch (Exception e) 
      { 
       throw e; 
      } 
     } 
    } 

lớp mẫu

public class Item 
{ 
    public string ItemCode { get; set; } 
    public string Cost { get; set; } 
    public override string ToString() 
    { 
     return "ItemCode : " + ItemCode + ", Cost : " + Cost; 
    } 
} 

Tạo DataTable

public DataTable getTable() 
{ 
    DataTable dt = new DataTable(); 
    dt.Columns.Add(new DataColumn("ItemCode", typeof(string))); 
    dt.Columns.Add(new DataColumn("Cost", typeof(string))); 
    DataRow dr; 
    for (int i = 0; i < 10; i++) 
    { 
     dr = dt.NewRow(); 
     dr[0] = "ItemCode" + (i + 1); 
     dr[1] = "Cost" + (i + 1); 
     dt.Rows.Add(dr); 
    } 
    return dt; 
} 

Bây giờ chúng ta có thể chuyển đổi DataTable này vào danh sách như dưới đây:

DataTable dt = getTable(); 
List<Item> lst = dt.ToCollection<Item>(); 
foreach (Item cn in lst) 
{ 
    Response.Write(cn.ToString() + "<BR/>"); 
} 

Hope sẽ giúp bạn

0

tôi được xây dựng trên logic Tomas Jansson để bao gồm một "Bỏ qua" thuộc tính. Điều này cho phép tôi thêm thuộc tính khác vào lớp đang được nạp mà không phá vỡ chính bản thân DataTable-To-Class.

Hoặc tôi cũng xem xét thêm một tham số riêng biệt giữ tên cột thực tế được đọc từ trong DataTable. Trong trường hợp đó thay vì sử dụng "row [property.Name]" thì bạn sẽ sử dụng hàng [attribute.Name] "hoặc tương tự như vậy cho thuộc tính cụ thể đó.

public static class DataTableExtensions 
{ 
    [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)] 
    public sealed class IgnoreAttribute : Attribute { public IgnoreAttribute() { } } 

    private static Dictionary<Type, IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>(); 

    public static IList<PropertyInfo> GetPropertiesForType<T>() 
    { 
     var type = typeof(T); 

     if (!typeDictionary.ContainsKey(typeof(T))) 
      typeDictionary.Add(type, type.GetProperties().ToList()); 

     return typeDictionary[type]; 
    } 

    public static IList<T> ToList<T>(this DataTable table) where T : new() 
    { 
     IList<PropertyInfo> properties = GetPropertiesForType<T>(); 
     IList<T> result = new List<T>(); 

     foreach (var row in table.Rows) 
      result.Add(CreateItemFromRow<T>((DataRow)row, properties)); 

     return result; 
    } 

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new() 
    { 
     T item = new T(); 

     foreach (var property in properties) 
     { 
      // Only load those attributes NOT tagged with the Ignore Attribute 
      var atr = property.GetCustomAttribute(typeof(IgnoreAttribute)); 
      if (atr == null) 
       property.SetValue(item, row[property.Name], null); 
     } 

     return item; 
    } 
} 
0

Đến muộn nhưng điều này có thể hữu ích. Có thể được gọi bằng cách sử dụng:

table.Map(); hoặc gọi bằng Func để lọc các giá trị.

Bạn thậm chí có thể thay đổi tên ánh xạ giữa thuộc tính loại và tiêu đề DataColumn bằng cách đặt thuộc tính trên thuộc tính.

[AttributeUsage(AttributeTargets.Property)] 
    public class SimppleMapperAttribute: Attribute 
    { 
     public string HeaderName { get; set; } 
    } 


    public static class SimpleMapper 
{ 
    #region properties 
    public static bool UseDeferredExecution { get; set; } = true; 
    #endregion 

#region public_interface 


    public static IEnumerable<T> MapWhere<T>(this DataTable table, Func<T, bool> sortExpression) where T:new() 
    { 
     var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties())).Where((t)=>sortExpression(t)); 
     return UseDeferredExecution ? result : result.ToArray(); 
    } 
    public static IEnumerable<T> Map<T>(this DataTable table) where T : new() 
    { 
     var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties())); 
     return UseDeferredExecution ? result : result.ToArray(); 
    } 
    #endregion 

#region implementation_details 
    private static T ConvertRow<T>(DataRow row, DataColumnCollection columns, System.Reflection.PropertyInfo[] p_info) where T : new() 
    { 
     var instance = new T(); 
     foreach (var info in p_info) 
     { 
      if (columns.Contains(GetMappingName(info))) SetProperty(row, instance, info);    
     } 
     return instance; 
    } 

    private static void SetProperty<T>(DataRow row, T instance, System.Reflection.PropertyInfo info) where T : new() 
    { 
     string mp_name = GetMappingName(info); 
     object value = row[mp_name]; 
     info.SetValue(instance, value); 
    } 

    private static string GetMappingName(System.Reflection.PropertyInfo info) 
    { 
     SimppleMapperAttribute attribute = info.GetCustomAttributes(typeof(SimppleMapperAttribute),true).Select((o) => o as SimppleMapperAttribute).FirstOrDefault(); 
     return attribute == null ? info.Name : attribute.HeaderName; 
    } 
#endregion 
} 
Các vấn đề liên quan