如何从C#中的SQL查询结果中填充类?

Pol*_*ial 11 c# database sql-server orm winforms

我有一个这样的课:

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; }
}
Run Code Online (Sandbox Code Playgroud)

我可以从我的数据库中获取这些细节,如下所示:

SELECT product_id, supplier_id, name, price, total_stock, pending_stock 
FROM products
WHERE product_id = ?
Run Code Online (Sandbox Code Playgroud)

我不想手动运行DataSetDataTable设置值.

我确信有一种方法可以使用某种绑定/映射机制来填充类,但我能找到的唯一东西是绑定到winforms组件或使用XAML.

是否有某种属性可以应用于我的属性/类,以便从查询行自动填充类?

Kub*_*tek 13

我已经决定提出另一个答案,它实际上扩展了Alex提供的答案(因此所有信用都给了他),但它为了column-name-2-property-name映射引入了属性.

首先需要保存列名的自定义属性:

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

该属性必须应用于要从数据库行填充的类的属性:

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; }
}
Run Code Online (Sandbox Code Playgroud)

除了该属性用于检索列名外,其余的就像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;
}
Run Code Online (Sandbox Code Playgroud)


mad*_*dd0 12

您需要自己映射属性或使用ORM(对象关系映射器).

Microsoft提供实体框架,但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 });
Run Code Online (Sandbox Code Playgroud)

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


Ale*_*lex 6

如果您不想利用 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;
}
Run Code Online (Sandbox Code Playgroud)

你像这样使用它:

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

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

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

警告我经常使用这种方法并且从来没有遇到任何问题,但它不能正常用于部分类。如果您使用部分模型,无论如何最好使用 ORM 框架。

  • 为什么不只按列名检索数据?`读者[modelProperties[i].Name]` (2认同)