在之前的一节 CLR学习笔记--Attribute 中,我们介绍了Attribute的用法,这一节,来写个具体Attribute应用场景,这个示例主要实现DataTable转换相应的实体,一般情况下,我们进行数据库查询,选用一个DataTable填充数据,但我们往往需要将DataTable进行一个相应的实体转换,例如经典的三层架构中,就存在这种情况,但三层的实现方式是,一个Entity对应一个转换方法,即我们已知一个Entity中的属性,创建Entity, 并将DataTable中的Column与Entity中的属性一一对应。但这种做法,每添加一个新的Entity,也必须添加一个相应的转换过程。
我们现在利用Attribute,在每个Entity中的属性应用一个ColumnAttribute实例,ColumnAttribute中有一个ColumnName定位参数和一个ColumnType命名参数。然后我们在DataTable转换Entity时,利用反射,获取每个属性的ColumnAttribute实例,并做相应的处理。
ColumnAttribute设计如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AttributeDemo
{
/// <summary>
/// 只适用于属性
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ColumnAttribute : Attribute
{
private string _ColumnName;
/// <summary>
/// 列名
/// </summary>
public string ColumnName
{
get { return _ColumnName; }
}
private Type _ColumnType = typeof(string);
/// <summary>
/// 列的数据类型 默认为string
/// </summary>
public Type ColumnType
{
get { return _ColumnType; }
set { _ColumnType = value; }
}
public ColumnAttribute(string columnName)
{
_ColumnName = columnName;
}
}
}
DataTable转换Entity代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection;
namespace AttributeDemo
{
internal static class Table2Entity<T> where T : class,new()
{
private static readonly Type type;
static Table2Entity()
{
type = typeof(T);
}
static T ConvertFromDataRow(DataRow dr)
{
T t = new T();
PropertyInfo[] pi = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
foreach (PropertyInfo p in pi)
{
//获取ColumnAttribute实例
ColumnAttribute ca = Attribute.GetCustomAttribute(p, typeof(ColumnAttribute)) as ColumnAttribute;
if (ca != null)
{
//如DataTable如不存列名,抛出异常
if (!dr.Table.Columns.Contains(ca.ColumnName))
throw new Exception("表中不存在列名: " + ca.ColumnName);
object value = Convert.ChangeType(dr[ca.ColumnName], ca.ColumnType);
p.SetValue(t, value, null);
}
}
return t;
}
public static List<T> ConvertFromTable(DataTable dt)
{
List<T> list = new List<T>();
for (int i = 0; i < dt.Rows.Count; i++)
{
list.Add(ConvertFromDataRow(dt.Rows[i]));
}
return list;
}
}
}
现在我们来定义一个Entity:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AttributeDemo
{
/// <summary>
/// Entity
/// </summary>
public class User
{
private string name;
[Column("Name")] //ColumnType默认为string
public string Name
{
get { return name; }
set { name = value; }
}
private Int32 age;
[Column("Age", ColumnType = typeof(Int32))]
public Int32 Age
{
get { return age; }
set { age = value; }
}
}
}
现在我们来测试一下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using System.Runtime.InteropServices;
namespace AttributeDemo
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Age", typeof(Int32));
DataRow dr = dt.NewRow();
dr["Name"] = "Jack";
dr["Age"] = 24;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Name"] = "Lucy";
dr["Age"] = 26;
dt.Rows.Add(dr);
List<User> lst = Table2Entity<User>.ConvertFromTable(dt);
foreach (User u in lst)
{
Console.WriteLine("User Name = {0},User Age ={1}", u.Name, u.Age);
}
Console.Read();
}
}
}
输出:
注意:这里这样的转换方式只是做为一个Attibute实际应用的一个小示例,实际上,像这种做法只是节省了代码量,但并没有实际意义上的效率提升,相反效率还会降低(因为我们利用了反射)