2012-07-27 89 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