Ответ 1
В итоге я использовал динамический прокси для свойств, чтобы я мог пометить свойства, написанные 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
}
Где-то в реализации прокси вызываем 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);
}
}
Идея здесь, так как JsonMediaTypeFormatter
преобразует входящий json, мы заменяем начальный пустой объект как прокси, который мы определили ранее.
Мы регистрируем этот конвертер в WebApiConfig.cs, как этот
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new MyJsonCreationConverter());
Теперь, когда наша модель заполняется из json вместо каждого объекта, полученного из DirtyPropertiesBase
, будет прокси с правильно заполненной коллекцией _dirtyList
. Теперь нам нужно только привязать каждую из этих моделей к объекту EF. Это достаточно просто с AutoMapper. Мы регистрируем каждую модель следующим образом:
Mapper.CreateMap<Model, Entity>().ForAllMembers(x => x.Condition(z => ((Model)z.Parent.SourceValue).IsPropertyDirty(z.MemberName)));
И тогда у вас есть свой обычный код отображения:
Entity current = _db.Entity.Single(x => x.Id == Id);
Mapper.Map(update, current);
_db.SaveChanges();
Это обеспечит обновление только Dirty-свойств.