2012-07-27 48 views
10

我有一類這樣的:如何從C#中的SQL查詢結果填充類?

public class Product 
{ 
    public int ProductId { get; private set; } 
    public int SupplierId { get; private set; } 

    public string Name { get; private set; } 
    public decimal Price { get; private set; } 
    public int Stock { get; private set; } 
    public int PendingStock { get; private set; } 
} 

我可以像這樣從我的數據庫獲取這些詳細信息:

SELECT product_id, supplier_id, name, price, total_stock, pending_stock 
FROM products 
WHERE product_id = ? 

我不想必須手動通過DataSet運行或DataTable設置值。我確定有一種方法可以使用某種綁定/映射機制來填充類,但唯一可以找到的是綁定到winforms組件或使用XAML。

是否有某種屬性我可以應用到我的屬性/類,讓類自動從查詢行填充?

回答

10

我已經決定提出另一個答案,它實際上是由Alex提供的答案的延伸(所有信用給他),但爲了列名2的屬性名映射引入了屬性。

首先,自定義屬性來保存需要的列名:

[AttributeUsage(AttributeTargets.Property, Inherited = true)] 
[Serializable] 
public class MappingAttribute : Attribute 
{ 
    public string ColumnName = null; 
} 

必須將屬性應用於類的這些屬性,將被從數據庫行填充:

public class Product 
{ 
    [Mapping(ColumnName = "product_id")] 
    public int ProductId { get; private set; } 

    [Mapping(ColumnName = "supplier_id")] 
    public int SupplierId { get; private set; } 

    [Mapping(ColumnName = "name")] 
    public string Name { get; private set; } 
    [Mapping(ColumnName = "price")] 
    public decimal Price { get; private set; } 
    [Mapping(ColumnName = "total_stock")] 
    public int Stock { get; private set; } 
    [Mapping(ColumnName = "pending_stock")] 
    public int PendingStock { get; private set; } 
} 

剩下的就像Alex提出的那樣,除了這個屬性是用來檢索列名的:

T MapToClass<T>(SqlDataReader reader) where T : class 
{ 
     T returnedObject = Activator.CreateInstance<T>(); 
     PropertyInfo[] modelProperties = returnedObject.GetType().GetProperties(); 
     for (int i = 0; i < modelProperties.Length; i++) 
     { 
      MappingAttribute[] attributes = modelProperties[i].GetCustomAttributes<MappingAttribute>(true).ToArray(); 

      if (attributes.Length > 0 && attributes[0].ColumnName != null) 
       modelProperties[i].SetValue(returnedObject, Convert.ChangeType(reader[attributes[0].ColumnName], modelProperties[i].PropertyType), null); 
     } 
     return returnedObject; 
} 
+0

我最終寫了幾乎這個。謝謝您的幫助! :) – Polynomial 2012-07-27 12:43:50

+0

只記得反射不是免費的,所以當映射超過幾行時,可能需要一點點緩存;) – madd0 2012-07-27 12:47:53

+0

我完全同意madd0(我在其他答案中說過同樣的話)。如果你想過度使用代碼 - 你需要提供一些緩存。 – 2012-07-27 12:49:37

0

,那麼你應該使用實體框架或LINQ to SQL的,如果你不想使用,那麼你需要映射/往裏面年自於實體框架

更多信息 http://msdn.microsoft.com/en-us/data/ef.aspx

更多LINQ的方式到SQL http://msdn.microsoft.com/en-us/library/bb386976.aspx

+0

你能提供一個實體框架的簡單例子嗎?我瀏覽了文檔,但對於如何實際使用它非常模糊。 – Polynomial 2012-07-27 11:11:34

+0

問題是,該類必須從實體創建,你不能使用自己的類,並從我的知識沒有自動的方式轉換,除非手動映射。 – JSantos 2012-07-27 11:14:45

2

我會使用LINQ to SQL和做如下:

public class Product 
{ 
    public int ProductId { get; private set; } 
    public int SupplierId { get; private set; } 
    public string Name { get; private set; } 
    public decimal Price { get; private set; } 
    public int Stock { get; private set; } 
    public int PendingStock { get; private set; } 

    public Product(int id) 
    { 
     using(var db = new MainContext()) 
     { 
      var q = (from c in product where c.ProductID = id select c).SingleOrDefault(); 
      if(q!=null) 
       LoadByRec(q);   
     } 
    } 
    public Product(product rec) 
    { 
     LoadByRec(q); 
    } 
    public void LoadByRec(product rec) 
    { 
     ProductId = rec.product_id; 
     SupplierID = rec.supplier_id; 
     Name = rec.name; 
     Price = rec.price; 
     Stock = rec.total_stock; 
     PendingStock = rec.pending_stock; 
    } 
} 
+0

你可以給這個添加一些解釋嗎?我沒有看到在'using'中的任何地方使用了'db',並且沒有提及查詢。有沒有辦法讓'LoadByRec'作爲一組屬性工作,而不是手動將它們映射到一個方法中? – Polynomial 2012-07-27 11:13:55

+0

@你必須插入一個數據上下文:http://msdn.microsoft.com/en-us/library/bb384470.aspx在這種情況下,你將命名'MainContext'。絕對玩弄它,它真的非常強大,並且一個巨大的倍頻器 – 2012-07-27 11:16:30

12

您需要自己映射屬性或使用ORM(對象關係映射器)。

微軟提供Entity Framework,但Dapper需要較少的開銷,並可能是一個可行的選項,根據您的要求。

在你的情況下,小巧玲瓏的代碼看起來是這樣的:

var query = @"SELECT product_id, supplier_id, name, price, total_stock, pending_stock 
FROM products 
WHERE product_id = @id"; 

var product = connection.Query<Product>(query, new { id = 23 }); 

爲了完整起見,要指出的是,我說的是小巧玲瓏的位置很重要,因爲這個問題的擔憂映射SQL結果對象。 EF和Linq to SQL也會這樣做,但他們也會做額外的工作,比如將Linq查詢轉換爲SQL語句,這也可能有用。

+2

適用於Dapper這裏 – 2012-07-27 11:18:45

1

默認情況下,在原始.NET Framework中沒有這樣的功能。你可以使用實體框架,但如果它不是一個好的解決方案,那麼另一種選擇就是反射機制。

  1. 創建一些自定義屬性類,該類可以爲您的類的每個公共屬性保存一個列名稱。

  2. 從數據庫中檢索記錄後,實例化Product對象並枚舉屬性。對於每個擁有自定義屬性的屬性 - 使用的SetValue根據自定義屬性中定義的列名更改值。

採取以下考慮:

  • 方案是相當的架空簡單的任務;它纔有意義,如果你有許多表和許多類,如Product - 並希望寫一個代碼自動初始化所​​有這些
  • 反射的開銷本身 - 所以一些緩存會從長遠來看
需要
+0

這聽起來很有趣。我會看看它。另外,我怎麼可能*不會*用* L *作爲他的頭像來回答他的答案!? :) – Polynomial 2012-07-27 12:04:07

+0

謝謝。 :]請看這裏的Alex's anwer,因爲他爲我在這裏提出的代碼提供了代碼,所以他的回答要好得多*除了我強烈建議在'Product'類'字段中使用自定義屬性。 – 2012-07-27 12:06:08

+0

是的,我現在正在研究它。乾杯:) – Polynomial 2012-07-27 12:08:33

4

如果您不想利用的ORM框架(實體框架等),你可以自己動手完成它:

T MapToClass<T>(SqlDataReader reader) where T : class 
{ 
     T returnedObject = Activator.CreateInstance<T>(); 
     List<PropertyInfo> modelProperties = returnedObject.GetType().GetProperties().OrderBy(p => p.MetadataToken).ToList(); 
     for (int i = 0; i < modelProperties.Count; i++) 
      modelProperties[i].SetValue(returnedObject, Convert.ChangeType(reader.GetValue(i), modelProperties[i].PropertyType), null); 
     return returnedObject; 
} 

你使用這樣的:

Product P = new Product(); // as per your example 
using(SqlDataReader reader = ...) 
{ 
while(reader.Read()) { P = MapToClass<Product(reader); /* then you use P */ } 
} 

唯一需要注意的是查詢中字段的順序(它必須與您的類中定義的屬性順序相匹配)。

所有你需要做的就是構建類,寫一個查詢,然後它會照顧「映射」。

警告我用這個方法很多,從未發生過任何問題,但它並不適用於部分類正常工作。如果你使用部分模型,反正使用ORM框架會更好。

+0

亞歷克斯,這是錯誤的。 GetProperties()不保證返回的PropertyInfo對象是按照聲明的順序。 – 2012-07-27 11:32:15

+0

我使用這種方法*很多*,並且他們總是以正確的方式啓動...無論如何,執行訂單非常簡單,我將藉此機會更新代碼。 – Alex 2012-07-27 11:33:28

+0

我也是這麼做的;]但是文檔很清楚:http://msdn.microsoft.com/en-us/library/aky14axb如果你沒有列名映射器,你將如何執行順序?屬性名稱? – 2012-07-27 11:36:36

-1
select 'public ' + case DATA_TYPE 
when 'varchar' then 'string' 
when 'nvarchar' then 'string' 
when 'DateTime' then 'DateTime' 
when 'bigint' then 'long' 
else DATA_TYPE end +' '+ COLUMN_NAME + ' {get; set;}' 
from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME ='YOUR TABLE NAME' ORDER BY ORDINAL_POSITION 
+0

請編輯更多信息。僅限代碼和「嘗試這個」的答案是不鼓勵的,因爲它們不包含可搜索的內容,也不解釋爲什麼有人應該「嘗試這個」。 – abarisone 2016-07-29 12:52:57

+0

這根本不回答問題。 – Polynomial 2016-07-29 13:45:27