从不同的表中返回不同的数据,字段名称也不相同,一般会写不同的控制器方法对每个表进行操作。但是在这里感觉很烦琐,因为除了数据不同,客户端的业务逻辑完全相同。以后还有可能增加其它表,也需要实现类似的业务逻辑。
想了想,决定采用动态对象来创建动态属性,微软提供了ExpandoObject 这样一个类可以实现我的需求。
先看代码:
/// <summary>
/// 根据查询语句返回json对象
/// </summary>
/// <param name="sql">查询语句</param>
/// <param name="fields">属性列表,属性应包含在查询语句的字段中</param>
/// <returns></returns>
private ActionResult GetDynamicParams(string sql, params string[] fields)
{
//创建Newtonsoft的json数组对象
JArray items = new JArray();
using (var reader = SqlHelper.ExecuteReader(SqlHelper.GetConnString(), CommandType.Text, sql))
{
while (reader.Read())
{
//动态属性集合
Dictionary<string, object> properties = new Dictionary<string, object>();
foreach (var field in fields)
{
properties.Add(field, reader[field]);
}
//动态对象
dynamic obj= new System.Dynamic.ExpandoObject();
//向动态对象中添加属性
foreach (var prop in properties)
{
((IDictionary<string, object>)obj).Add(prop.Key, prop.Value);
}
//创建Newtonsoft的json对象
JObject jobj = new JObject();
//从动态对象中读取数据并放入到json对象中
foreach (var property in (IDictionary<String, Object>)obj)
{
jobj.Add(property.Key, property.Value.ToString());
}
items.Add(jobj);
}
}
//返回json数据
return Content(items.ToString());
}
不要问我为什么还在使用SqlDataReader从数据库读取数据,这个不是重点……
ExpandoObject对象可以直接添加动态属性,例如:
dynamic obj= new System.Dynamic.ExpandoObject();
obj.Name = "张三";
obj.Age = 20;
Console.WriteLine(obj.Name);
Console.WriteLine(obj.Age);
//输出
张三
20
但是在这个需求中不能这么做,因为属性名称是动态的,来自于查询结果中的字段,在这里由方法的可变参数fields提供。假设我们调用时是这样:GetDynamicParams(sql,"StyleNo","StyleDesc")
那么要返回如下数据:
[
{
StyleNo: "00432",
StyleDesc: "休闲牛仔裤"
},
{
StyleNo: "00516",
StyleDesc: "休闲T恤"
}
]
如果调用GetDynamicParams(sql,"StoreNo","StoreName")
返回数据如下:
[
{
StoreNo: "A",
StoreName: "一号仓库"
},
{
StoreNo: "B",
StoreName: "二号仓库"
}
]
ExpandoObject实现了IDictionary<TKey,TValue>接口,可以利用接口的Add方法向对象动态添加属性。
//动态属性集合
Dictionary<string, object> properties = new Dictionary<string, object>();
foreach (var field in fields)
{
properties.Add(field, reader[field]);
}
首先声明一个键值对集合,根据参数fields的值,将数据库中的数据添加到键值对集合中。
//向动态对象中添加属性
foreach (var prop in properties)
{
((IDictionary<string, object>)obj).Add(prop.Key, prop.Value);
}
每个键值对就是一个属性及属性的值,循环完成之后,obj的内容就是这样:
到这个时候,动态创建对象和属性就已经完成了。
但是obj不能直接序列化成json数据,如果这时候试图返回json数据:
return json(obj);
会报以下错误:
InvalidCastException: Unable to cast object of type '<GetExpandoEnumerator>d__51' to type 'System.Collections.IDictionaryEnumerator'.
所以我们借助Newtonsoft的JObject对象来生成json数据
//从动态对象中读取数据并放入到json对象中
foreach (var property in (IDictionary<String, Object>)obj)
{
jobj.Add(property.Key, property.Value.ToString());
}
这个循环把数据从动态对象中读取出来并放到json对象中。因为属性名称是动态的,我们没法使用对象.属性
的方式来访问属性的值。如果使用反射,你会发现obj.GetType().GetProperties()
的返回值是null,GetProperty(属性名)
的返回值也同样是null。因此,还是要利用IDictionary<String, Object>接口来遍历属性及其值。