我有一个这样的课程:
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。
是否有某种属性可以应用于我的属性/类,以便从查询行自动填充该类?
答案 0 :(得分:13)
我已经决定提出另一个答案,它实际上扩展了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;
}
答案 1 :(得分:12)
您需要自己映射属性或使用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语句,这也可能有用。
答案 2 :(得分: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框架。
答案 3 :(得分: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;
}
}
答案 4 :(得分:1)
默认情况下,在原始.NET Framework中没有此类功能。您可以使用Entity Framework,但如果它不适合您,那么另一种选择就是反射机制。
创建一些自定义属性类,该类可以保存类的每个公共属性的列名。
从数据库中检索记录后,实例化Product
类的对象并枚举属性。对于具有自定义属性的每个属性 - 使用SetValue
的{{1}}根据自定义属性中定义的列名更改值。
考虑以下因素:
PropertyInfo
- 并且希望编写一个代码来自动初始化所有这些类时才有意义答案 5 :(得分:1)
一种可能的解决方案:
在SQL查询中,您可以使用"PATH Mode with FOR XML "子句。
查询结果将是一个XML,您可以deserialize directly to C# objects。
它也适用于大型嵌套SQL查询。
答案 6 :(得分:0)
然后你应该使用Entity Framework或Linq To SQL,如果你不想使用它,那么你需要自己映射/填充它
有关实体框架的更多信息 http://msdn.microsoft.com/en-us/data/ef.aspx
有关Linq to SQL的更多信息 http://msdn.microsoft.com/en-us/library/bb386976.aspx
答案 7 :(得分:-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