And*_*ykh 23 .net c# json asp.net-web-api
这是场景.有一个web api put调用来更改sql server数据库中的对象.我们只想更改数据库对象上的字段,如果它们是在webapi调用json上显式指定的.例如:
{ "Name":"newName", "Colour":null }
Run Code Online (Sandbox Code Playgroud)
这应该将Name字段更改为"newName"和"Color"字段为null.与这个json相反:
{ "Name":"newName" }
Run Code Online (Sandbox Code Playgroud)
应该只更改名称字段,保持旧的颜色值不变.
WebApi检测属性是否通过的好方法是什么?
如果我像这样定义我的方法:
[HttpPut]
[Route("/item/{id}")]
public void ChangeItem(int id, Item item)
{
...
}
Run Code Online (Sandbox Code Playgroud)
item.Colour在任何一种情况下都将为null.请注意,我用各种这里的数据类型和物业工作Colour的例子是int,string,DateTime,Guid,等.
我知道我可以使用[FromBody]属性获取原始json,然后自己解析它,但似乎默认绑定器已经完成了大部分工作(包括验证),所以我很好奇我如何重用它,但也实现了我想要的是.什么是最简单的方法?
更新:
我想澄清一下,我的是"偶尔联系"的情景.也就是说,使用API的设备大多数时间都不在网络覆盖范围内,并且它们会不时使用API进行同步.
实际上,这意味着同步所需的大多数数据被聚合为零或一个"推送更新到服务器"调用,然后是"从服务器获取最新状态"调用.使用后端的Sql Server和EF导致几个不同的(有时是无关的)实体包含在单个json中.例如:
class TaskData
{
public IList<User> AssignedUsers {get; set;}
public IList<Product> Products {get; set;}
public Task Task {get; set}
}
Run Code Online (Sandbox Code Playgroud)
此外,用于为GET调用生成json的模型类与EF Entites是分开的,因为数据库模式与API对象模型不完全匹配.
我最终对属性使用动态代理,以便我可以将 写入的属性标记为JsonMediaTypeFormatter“脏”。我使用了稍微修改过的yappi(实际上不必修改它,只是想 - 如果下面的代码与 yappi 样本/API 不完全匹配,请提及这一点)。我猜您可以使用您最喜欢的动态代理库。只是为了好玩,我尝试将其移植到NProxy.Core,但这不起作用,因为由于某种原因 json.net 拒绝写入NProxy.Core生成的代理。
所以它是这样工作的。我们有一个基类:
public class DirtyPropertiesBase
{
...
// most of these come from Yappi
public static class Create<TConcept> where TConcept : DirtyPropertiesBase
{
public static readonly Type Type =PropertyProxy.ConstructType<TConcept, PropertyMap<TConcept>>(new Type[0], true);
public static Func<TConcept> New = Constructor.Compile<Func<TConcept>>(Type);
}
private readonly List<string> _dirtyList = new List<string>();
protected void OnPropertyChanged(string name)
{
if (!_dirtyList.Contains(name))
{
_dirtyList.Add(name);
}
}
public bool IsPropertyDirty(string name)
{
return _dirtyList.Contains(name);
}
...
// some more Yappi specific code that calls OnPropertyChanged
// when a property setter is called
}
Run Code Online (Sandbox Code Playgroud)
我们调用代理实现中的某个位置OnPropertyChanged,以便记住写入了哪些属性。
然后我们有我们的习惯JsonCreationConverter:
class MyJsonCreationConverter : JsonConverter
{
private static readonly ConcurrentDictionary<Type, Func<DirtyPropertiesBase>> ContructorCache = new ConcurrentDictionary<Type, Func<DirtyPropertiesBase>>();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException("MyJsonCreationConverter should only be used while deserializing.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
Func<DirtyPropertiesBase> constructor = ContructorCache.GetOrAdd(objectType, x =>
(Func<DirtyPropertiesBase>)typeof(DirtyPropertiesBase.Create<>).MakeGenericType(objectType).GetField("New").GetValue(null));
DirtyPropertiesBase value = constructor();
serializer.Populate(reader, value);
return value;
}
public override bool CanConvert(Type objectType)
{
return typeof (DirtyPropertiesBase).IsAssignableFrom(objectType);
}
}
Run Code Online (Sandbox Code Playgroud)
这里的想法是,在JsonMediaTypeFormatter转换传入的 json 时,我们将初始的空对象替换为我们之前定义的代理。
我们像这样在 WebApiConfig.cs 中注册这个转换器
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new MyJsonCreationConverter());
Run Code Online (Sandbox Code Playgroud)
现在,当我们的模型从 json 填充而不是从派生的每个对象时,DirtyPropertiesBase将有一个具有正确填充_dirtyList集合的代理。现在我们只需要将这些模型映射回 EF 实体即可。使用AutoMapper这很简单。我们像这样注册每个模型:
Mapper.CreateMap<Model, Entity>().ForAllMembers(x => x.Condition(z => ((Model)z.Parent.SourceValue).IsPropertyDirty(z.MemberName)));
Run Code Online (Sandbox Code Playgroud)
然后你就有了常用的映射代码:
Entity current = _db.Entity.Single(x => x.Id == Id);
Mapper.Map(update, current);
_db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
这将确保仅更新脏属性。
| 归档时间: |
|
| 查看次数: |
4926 次 |
| 最近记录: |