您的当前位置:首页正文

在控制器中创建动态对象和动态属性并返回Json数据

来源:华拓网

从不同的表中返回不同的数据,字段名称也不相同,一般会写不同的控制器方法对每个表进行操作。但是在这里感觉很烦琐,因为除了数据不同,客户端的业务逻辑完全相同。以后还有可能增加其它表,也需要实现类似的业务逻辑。

想了想,决定采用动态对象来创建动态属性,微软提供了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>接口来遍历属性及其值。