[英]How can I populate my sql query results into a C# Datagridviewer
[英]How can I populate a class from the results of a SQL query in C#?
我有這樣的課:
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。
是否有某種屬性可以應用於我的屬性/類以使該類從查詢行中自動填充?
我決定提出另一個答案,它實際上是對 Alex 提供的答案的擴展(所以所有的功勞都歸於他),但是為了 column-name-2-property-name 映射,它引入了屬性。
首先需要自定義屬性來保存列名:
[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;
}
您需要自己映射屬性或使用 ORM(對象關系映射器)。
Microsoft 提供Entity Framework ,但Dapper需要較少的開銷,並且根據您的要求可能是一個可行的選擇。
在您的情況下,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 });
為了完整起見,重要的是要指出我在這里談論的是 Dapper,因為問題涉及將 SQL 結果映射到對象。 EF 和 Linq to SQL 也會這樣做,但它們也會做一些額外的事情,比如將 Linq 查詢轉換為 SQL 語句,這也可能很有用。
如果您不想利用 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 框架會好得多。
我會使用 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;
}
}
默認情況下,原始 .NET Framework 中沒有此類功能。 您可以使用實體框架,但如果它對您來說不是一個好的解決方案,那么另一種選擇是反射機制。
創建一些自定義屬性類,該類可以為類的每個公共屬性保存一個列名。
從數據庫中檢索記錄后,實例化Product
類的對象並枚舉屬性。 對於具有自定義屬性的每個屬性 - 使用PropertyInfo
的SetValue
根據自定義屬性中定義的列名稱更改值。
考慮以下因素:
Product
)時才有意義 - 並且希望編寫一個代碼來自動初始化所有這些一種可能的解決方案:
在 SQL 查詢中,您可以使用“PATH Mode with FOR XML”子句。
查詢的結果將是一個 XML,您可以將其直接反序列化為 C# 對象。
它也適用於大型嵌套 SQL 查詢。
然后你應該使用實體框架或 Linq To SQL,如果你不想使用它,那么你需要自己映射/填充它
有關實體框架的更多信息http://msdn.microsoft.com/en-us/data/ef.aspx
有關 Linq to SQL 的更多信息http://msdn.microsoft.com/en-us/library/bb386976.aspx
使用實體框架,您可以使用 SqlQuery 方法來填充不是表或視圖的類: context.Database.SqlQuery(sql, parameterList)
當然你需要使用EF。 :-)
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.