提问者:小点点

在MVC 5中构建动态JSON响应——如何将属性附加到结果集中?


我正在将对象数组返回到基于相册呈现幻灯片的页面。

我从数据库中取出我的照片。

在我作为结果返回这个数组之前,我想添加一个“缩略图”url属性。AlumPicture上不存在此属性,但我希望它在响应中。

这说明了这个想法:

List<AlbumPicture> pics = db.AlbumPictures.Where(p => p.AlbumID == album.ID).OrderBy(p => p.RankOrder).ToList();
foreach(AlbumPicture p in pics)
{
    p.AddPropertyThatDoesntExist("Thumbnail", ThumbManager.GetThumb(p.ID));
}

return Json(pics, JsonRequestBehavior.AllowGet);

将此JSON字段添加到我的结果集中的最优雅方法是什么?

这个问题太基础了,很可能是重复的。然而,我谷歌了10分钟,只能找到依赖于第三方库的janky解决方案。我对当前的“最佳实践”感兴趣。

可能的重复:如何从控制器向Json响应动态添加更多属性。但是,该答案将使动态添加的字段成为生成的JSON中我的AlumPicture属性的叔叔而不是兄弟。


共3个答案

匿名用户

目前,C# 4.0支持动态对象,所以你可以在运行时使用动态对象来添加你的属性。

首先,将这个扩展方法添加到代码中:

public static class DynamicExtensions
{
    public static dynamic ToDynamic(this object value)
    {
        IDictionary<string, object> expando = new ExpandoObject();

        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
            expando.Add(property.Name, property.GetValue(value));

        return expando as ExpandoObject;
    }
}

然后,将您的代码更改为这样的内容

var pics = db.AlbumPictures.Where(p => p.AlbumID == album.ID)
          .OrderBy(p => p.RankOrder)
          .Select(p=> 
                  { dynamic copy = p.ToDynamic();
                    copy.Thumbnail = ThumbManager.GetThumb(p.ID); //You can add more dynamic properties here
                    return copy;
                  })
          .ToList();

return Json(pics, JsonRequestBehavior.AllowGet);

匿名用户

我同意上面的大部分答案,使用动态对象或expando对象你可以添加额外的属性等。然而,这个任务很简单,在我看来,你不应该向用户界面或消费者公开你的实体对象。我只需创建一个简单的视图模型,并将其传递回调用者,如下所示:

Public Class Album{
   string PhotoUrl {get; set;}
   string ThumbnailUrl {get; set;}
}


List<AlbumPicture> pics = db.AlbumPictures.Where(p => p.AlbumID == album.ID).OrderBy(p => p.RankOrder).ToList();
List<Album> albums = new List<Album>();
foreach(AlbumPicture p in pics)
{
    albums.add(new Album(){
      PhotoUrl = p.PhotoUrl,//your photo url
      ThumbnailUrl = p.ThumbnailUrl
    });
}
 return Json(albums, JsonRequestBehavior.AllowGet);

匿名用户

在我看来,conanak99提供的答案是正确的方法。无论如何,像这样的扩展通常隐藏在您的核心实用程序中,因此他的答案具有可重用的性质。如果您确定只会将其用于此目的,则可以将其重构为

public static dynamic AppendProperty<T>(this object value, string propertyName, T newValue)
    {
        IDictionary<string, object> expando = new ExpandoObject();

        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
            expando.Add(property.Name, property.GetValue(value));

        expando.Add(propertyName, newValue);

        return expando as ExpandoObject;
    }

这反过来会产生以下代码来做你想做的事:

 var pics = db.AlbumPictures.Where(p => p.AlbumID == album.ID)
                  .OrderBy(p => p.RankOrder)
                  .Select(p =>
                  {
                      return p.AppendProperty("Thumbnail", ThumbManager.GetThumb(p.ID));
                  })
                  .ToList();

更新-

如果您正在使用automapper,或者愿意安装它,这是另一种可以用来实现相同结果的变通方法。

首先,创建Album的扩展类,并添加属性

class AlbumExt : Album
    {
        public string Thumbnail { get; set; }
    }

然后在select语句中创建扩展类的新实例,使用automapper(如果没有太多,则手动复制)将属性值复制到新类中,并设置缩略图属性。

var pics = db.AlbumPictures.Where(p => p.AlbumID == album.ID)
                  .OrderBy(p => p.RankOrder)
                  .Select(p =>
                  {
                      AlbumExt tmp = new AlbumExt();
                      AutoMapper.Mapper.DynamicMap(p, tmp);
                      tmp.Thumbnail = "new prop value";
                      return tmp;
                  })
                  .ToList();

自动映射器基本上只是使用默认约定将属性值从一个类复制到另一个类,如果需要,可以覆盖这些约定。