using System;
using SalesApi.Core.Abstractions.DomainModels;
namespace SalesApi.ViewModels
public class CustomerViewModel: EntityBase
public string Company { get; set; }
public string Name { get; set; }
public DateTimeOffset EstablishmentTime { get; set; }
}
还需要一个Extension Method可以把对象按照需要的属性转化成dynamic类型:
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
namespace SalesApi.Shared.Helpers
public static class ObjectExtensions
public static ExpandoObject ToDynamic<TSource>(this TSource source, string fields = null)
if (source == null)
throw new ArgumentNullException("source");
var dataShapedObject = new ExpandoObject();
if (string.IsNullOrWhiteSpace(fields))
// 所有的 public properties 应该包含在ExpandoObject里
var propertyInfos = typeof(TSource).GetProperties(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
foreach (var propertyInfo in propertyInfos)
// 取得源对象上该property的值
var propertyValue = propertyInfo.GetValue(source);
// 为ExpandoObject添加field
((IDictionary<string, object>)dataShapedObject).Add(propertyInfo.Name, propertyValue);
return dataShapedObject;
// field是使用 "," 分割的, 这里是进行分割动作.
var fieldsAfterSplit = fields.Split(',');
foreach (var field in fieldsAfterSplit)
var propertyName = field.Trim();
// 使用反射来获取源对象上的property
// 需要包括public和实例属性, 并忽略大小写.
var propertyInfo = typeof(TSource).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo == null)
throw new Exception($"没有在‘{typeof(TSource)}’上找到‘{propertyName}’这个Property");
// 取得源对象property的值
var propertyValue = propertyInfo.GetValue(source);
// 为ExpandoObject添加field
((IDictionary<string, object>)dataShapedObject).Add(propertyInfo.Name, propertyValue);
return dataShapedObject;
}
注意: 这里的逻辑是如果没有选择需要的属性的话, 那么就返回所有合适的属性.
然后在CustomerController里面:
首先创建为对象添加link的方法:
private IEnumerable<LinkViewModel> CreateLinksForCustomer(int id, string fields = null)
var links = new List<LinkViewModel>();
if (string.IsNullOrWhiteSpace(fields))
links.Add(
new LinkViewModel(_urlHelper.Link("GetCustomer", new { id = id }),
"self",
"GET"));
links.Add(
new LinkViewModel(_urlHelper.Link("GetCustomer", new { id = id, fields = fields }),
"self",
"GET"));
links.Add(
new LinkViewModel(_urlHelper.Link("DeleteCustomer", new { id = id }),
"delete_customer",
"DELETE"));
links.Add(
new LinkViewModel(_urlHelper.Link("CreateCustomer", new { id = id }),
"create_customer",
"POST"));
return links;
}
针对返回一个对象, 添加了本身的连接, 添加的连接 以及 删除的连接.
然后修改Get和Post的Action:
[HttpGet]
[Route("{id}", Name = "GetCustomer")]
public async Task<IActionResult> Get(int id, string fields)
var item = await _customerRepository.GetSingleAsync(id);
if (item == null)
return NotFound();
var customerVm = Mapper.Map<CustomerViewModel>(item);
var links = CreateLinksForCustomer(id, fields);
var dynamicObject = customerVm.ToDynamic(fields) as IDictionary<string, object>;
dynamicObject.Add("links", links);
return Ok(dynamicObject);
[HttpPost(Name = "CreateCustomer")]
public async Task<IActionResult> Post([FromBody] CustomerViewModel customerVm)
if (customerVm == null)
return BadRequest();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var newItem = Mapper.Map<Customer>(customerVm);
_customerRepository.Add(newItem);
if (!await UnitOfWork.SaveAsync())
return StatusCode(500, "保存时出错");
var vm = Mapper.Map<CustomerViewModel>(newItem);
var links = CreateLinksForCustomer(vm.Id);
var dynamicObject = vm.ToDynamic() as IDictionary<string, object>;
dynamicObject.Add("links", links);
return CreatedAtRoute("GetCustomer", new { id = dynamicObject["Id"] }, dynamicObject);
}
services.AddMvc(options =>
options.ReturnHttpNotAcceptable = true;
// the default formatter is the first one in the list.
options.OutputFormatters.Remove(new XmlDataContractSerializerOutputFormatter());
// set authorization on all controllers or routes
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
.AddJsonOptions(options =>
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
.AddFluetValidations();
然后再试试:
OK.
返回集合
首先编写创建links的方法:
private IEnumerable<LinkViewModel> CreateLinksForCustomers(string fields = null)
var links = new List<LinkViewModel>();
if (string.IsNullOrWhiteSpace(fields))
links.Add(
new LinkViewModel(_urlHelper.Link("GetAllCustomers", new { fields = fields }),
"self",
"GET"));
links.Add(
new LinkViewModel(_urlHelper.Link("GetAllCustomers", new { }),
"self",
"GET"));
return links;
}
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
namespace SalesApi.Shared.Helpers
public static class IEnumerableExtensions
public static IEnumerable<ExpandoObject> ToDynamicIEnumerable<TSource>(this IEnumerable<TSource> source, string fields)
if (source == null)
throw new ArgumentNullException("source");
var expandoObjectList = new List<ExpandoObject>();
var propertyInfoList = new List<PropertyInfo>();
if (string.IsNullOrWhiteSpace(fields))
var propertyInfos = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
propertyInfoList.AddRange(propertyInfos);
var fieldsAfterSplit = fields.Split(',');
foreach (var field in fieldsAfterSplit)
var propertyName = field.Trim();
var propertyInfo = typeof(TSource).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo == null)
throw new Exception($"Property {propertyName} wasn't found on {typeof(TSource)}");
propertyInfoList.Add(propertyInfo);
foreach (TSource sourceObject in source)
var dataShapedObject = new ExpandoObject();
foreach (var propertyInfo in propertyInfoList)
var propertyValue = propertyInfo.GetValue(sourceObject);
((IDictionary<string, object>)dataShapedObject).Add(propertyInfo.Name, propertyValue);
expandoObjectList.Add(dataShapedObject);
return expandoObjectList;
}
注意: 反射的开销很大, 注意性能.
然后修改GetAll方法:
[HttpGet(Name = "GetAllCustomers")]
public async Task<IActionResult> GetAll(string fields)
var items = await _customerRepository.GetAllAsync();
var results = Mapper.Map<IEnumerable<CustomerViewModel>>(items);
var dynamicList = results.ToDynamicIEnumerable(fields);
var links = CreateLinksForCustomers(fields);
var dynamicListWithLinks = dynamicList.Select(customer =>
var customerDictionary = customer as IDictionary<string, object>;
var customerLinks = CreateLinksForCustomer(
(int)customerDictionary["Id"], fields);
customerDictionary.Add("links", customerLinks);
return customerDictionary;
var resultWithLink = new {
Value = dynamicListWithLinks,
Links = links