-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPatchable.cs
158 lines (130 loc) · 5.73 KB
/
Patchable.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
using Patchable.Helpers;
using Patchable.Internals;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
namespace Patchable
{
public sealed class Patchable<TEntity>
: IDictionary<string, object> where TEntity : class
{
private readonly Dictionary<string, object> _valueDictionary = new Dictionary<string, object>();
private List<PatchablePropInfo> _entityProperties = new List<PatchablePropInfo>();
public Patchable()
{
}
/// <summary>
/// Patch against target entity (other type).
/// </summary>
/// <typeparam name="TTarget">Type must have name-wise comparable properties of <typeparamref name="TEntity"/>.</typeparam>.
/// <param name="entity">Entity to patch.</param>
/// <param name="options">Optional. Options for patch operation.</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidPropertyException"></exception>
public void Patch<TTarget>(TTarget entity, PatchableOptions options = null)
where TTarget : class
{
if (entity is null)
throw new ArgumentNullException(nameof(entity));
SetPropertiesValue(entity, options);
}
/// <summary>
/// Patch against entity.
/// </summary>
/// <param name="entity">Entity to patch.</param>
/// <param name="options">Optional. Options for patch operation.</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidPropertyException"></exception>
public void Patch(TEntity entity, PatchableOptions options = null)
=> Patch<TEntity>(entity, options);
private void SetPropertiesValue<TTarget>(TTarget entity, PatchableOptions options = null)
where TTarget : class
{
var cache = PatchableCache.GetOrCreateCache();
_entityProperties = cache.GetEntityProperties<TTarget>();
var patchOptions = options ?? new PatchableOptions(false);
foreach (var key in _valueDictionary.Keys)
{
var property = _entityProperties
.FirstOrDefault(p => p.Name.Equals(key, StringComparison.CurrentCultureIgnoreCase));
// if event delegation ignore property, continue;
var eventArgs = new EvaluatePropertyEventArgs<TTarget>(property, entity);
patchOptions.OnEvaluateProperty(eventArgs);
if (eventArgs.IgnoreProperty)
continue;
// default
if (property is null &&
patchOptions.IgnoreInvalidProperties == false)
{
throw new InvalidPropertyException(typeof(TEntity), key);
}
else if (property is null &&
patchOptions.IgnoreInvalidProperties == true)
{
continue; // ignored
}
var value = _valueDictionary[key];
if (TypeHelper.IsNullable(property.Property) == false &&
value is null)
{
throw new ArgumentNullException($"{key} value is null, however property does not allow null.");
}
try
{
var jsonElement = (JsonElement)_valueDictionary[key];
var valueValue = JsonHelper.ToObject(jsonElement, property.Property.PropertyType);
property.Property.SetValue(entity, valueValue);
}
catch (JsonException e)
{
throw new FormatException($"Invalid format patching property name '{key}'.", e);
}
catch
{
throw;
}
}
}
#region IDictionary Interface
public object this[string key] {
get {
return _valueDictionary[key];
}
set {
_valueDictionary[key] = value;
}
}
public ICollection<string> Keys => _valueDictionary.Keys;
public ICollection<object> Values => _valueDictionary.Values;
public int Count => _valueDictionary.Count;
public bool IsReadOnly => ((IDictionary<string, object>)_valueDictionary).IsReadOnly;
public void Add(string key, object value)
=> _valueDictionary.Add(key, value);
public void Add(KeyValuePair<string, object> item)
=> _valueDictionary.Add(item.Key, item.Value);
public void Clear()
=> _valueDictionary.Clear();
public bool Contains(KeyValuePair<string, object> item)
=> _valueDictionary.Contains(item);
public bool ContainsKey(string key)
=> _valueDictionary.ContainsKey(key);
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
((IDictionary<string, object>)_valueDictionary).CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
=> _valueDictionary.GetEnumerator();
public bool Remove(string key)
=> _valueDictionary.Remove(key);
public bool Remove(KeyValuePair<string, object> item)
=> _valueDictionary.Remove(item.Key);
public bool TryGetValue(string key, [MaybeNullWhen(false)] out object value)
=> _valueDictionary.TryGetValue(key, out value);
IEnumerator IEnumerable.GetEnumerator()
=> _valueDictionary.GetEnumerator();
#endregion
}
}