如何从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 = ? 

我不想手动运行DataSetDataTable来设置值。

我确信有一种方法可以使用某种绑定/映射机制来填充类,但我能找到的唯一东西是绑定到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(SqlDataReader reader) where T : class { T returnedObject = Activator.CreateInstance(); PropertyInfo[] modelProperties = returnedObject.GetType().GetProperties(); for (int i = 0; i < modelProperties.Length; i++) { MappingAttribute[] attributes = modelProperties[i].GetCustomAttributes(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(query, new { id = 23 }); 

为了完整起见,重要的是要指出我在这里谈论Dapper,因为问题涉及将SQL结果映射到对象。 EF和Linq to SQL也会这样做,但它们也会做其他的事情,比如将Linq查询转换为SQL语句,这也可能有用。

如果您不想利用ORM框架(entity framework等),您可以手动完成:

 T MapToClass(SqlDataReader reader) where T : class { T returnedObject = Activator.CreateInstance(); List 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 

唯一需要注意的是查询中字段的顺序(它必须与属性中的属性顺序相匹配)。

您需要做的就是构建类,编写查询,然后它将处理“映射”。

警告我经常使用这种方法,从来没有遇到任何问题,但它对于部分类不能正常工作 。 如果你来部分模型,你最好还是使用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中没有此类function。 您可以使用entity framework,但如果它对您来说不是一个好的解决方案,那么另一种选择就是reflection机制。

  1. 创建一些自定义属性类,该类可以保存类的每个公共属性的列名。

  2. 从数据库检索记录后,实例化Product类的对象并枚举属性。 对于具有自定义属性的每个属性 – 使用PropertyInfo SetValue根据自定义属性中定义的列名更改值。

考虑以下因素:

  • 对于简单的任务,解决方案是非常有用的; 只有当你有许多表和许多像Product这样的类时才有意义 – 并希望编写一个代码来自动初始化所​​有这些
  • reflection本身就是一种开销 – 因此从长远来看需要一些缓存

一种可能的方案:

在SQL查询中,您可以使用“PATH模式与FOR XML”子句。

查询的结果将是一个XML,您可以直接反序列化为C#对象 。

它也适用于大型嵌套SQL查询。

然后你应该使用Entity Framework或Linq To SQL,如果你不想使用它,那么你需要映射/填充它自己

有关entity framework的更多信息,请访问http://msdn.microsoft.com/en-us/data/ef.aspx

有关Linq to SQL的更多信息http://msdn.microsoft.com/en-us/library/bb386976.aspx

 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