Skip to content

Commit 9db172f

Browse files
committed
Allow for custom property conversion
1 parent 66adca2 commit 9db172f

File tree

5 files changed

+164
-61
lines changed

5 files changed

+164
-61
lines changed

src/TableStorage.Abstractions.TableEntityConverters/EntityConvert.cs

Lines changed: 78 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace TableStorage.Abstractions.TableEntityConverters
1111
public static class EntityConvert
1212
{
1313
private static JsonSerializerSettings _defaultJsonSerializerSettings = new JsonSerializerSettings();
14-
14+
1515
/// <summary>
1616
/// Json fields will use be serialized/deserialized with these provided settings when jsonSerializerSettings are
1717
/// not explicitly passed into ToTableEntity/FromTableEntity
@@ -25,25 +25,31 @@ public static void SetDefaultJsonSerializerSettings (JsonSerializerSettings json
2525
public static DynamicTableEntity ToTableEntity<T>(this T o, string partitionKey, string rowKey,
2626
params Expression<Func<T, object>>[] ignoredProperties)
2727
{
28-
return ToTableEntity(o, partitionKey, rowKey, _defaultJsonSerializerSettings, ignoredProperties);
28+
return ToTableEntity(o, partitionKey, rowKey, _defaultJsonSerializerSettings, default, ignoredProperties);
2929
}
30-
public static DynamicTableEntity ToTableEntity<T>(this T o, string partitionKey, string rowKey, JsonSerializerSettings jsonSerializerSettings, params Expression<Func<T, object>>[] ignoredProperties)
30+
public static DynamicTableEntity ToTableEntity<T>(this T o, string partitionKey, string rowKey,
31+
JsonSerializerSettings jsonSerializerSettings,
32+
PropertyConverters<T> propertyConverters,
33+
params Expression<Func<T, object>>[] ignoredProperties)
3134
{
3235
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
3336
var type = typeof(T);
3437
var properties = GetProperties(type);
3538
RemoveIgnoredProperties(properties, ignoredProperties);
36-
return CreateTableEntity(o, properties, partitionKey, rowKey, jsonSerializerSettings);
39+
return CreateTableEntity(o, properties, partitionKey, rowKey, jsonSerializerSettings, propertyConverters);
3740
}
3841

3942
public static DynamicTableEntity ToTableEntity<T>(this T o, Expression<Func<T, object>> partitionProperty,
40-
Expression<Func<T, object>> rowProperty, params Expression<Func<T, object>>[] ignoredProperties)
43+
Expression<Func<T, object>> rowProperty,
44+
params Expression<Func<T, object>>[] ignoredProperties)
4145
{
42-
return ToTableEntity(o, partitionProperty, rowProperty, _defaultJsonSerializerSettings, ignoredProperties);
46+
return ToTableEntity(o, partitionProperty, rowProperty, _defaultJsonSerializerSettings, null, ignoredProperties);
4347
}
4448

4549
public static DynamicTableEntity ToTableEntity<T>(this T o, Expression<Func<T, object>> partitionProperty,
46-
Expression<Func<T, object>> rowProperty, JsonSerializerSettings jsonSerializerSettings, params Expression<Func<T, object>>[] ignoredProperties)
50+
Expression<Func<T, object>> rowProperty, JsonSerializerSettings jsonSerializerSettings,
51+
PropertyConverters<T> propertyConverters = null,
52+
params Expression<Func<T, object>>[] ignoredProperties)
4753
{
4854
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
4955
var type = typeof(T);
@@ -64,11 +70,10 @@ public static DynamicTableEntity ToTableEntity<T>(this T o, Expression<Func<T, o
6470
properties.Remove(partitionProp);
6571
properties.Remove(rowProp);
6672
RemoveIgnoredProperties(properties, ignoredProperties);
67-
6873
var partitionKey = partitionProp.GetValue(o).ToString();
6974
var rowKey = rowProp.GetValue(o).ToString();
7075

71-
return CreateTableEntity(o, properties, partitionKey, rowKey, jsonSerializerSettings);
76+
return CreateTableEntity(o, properties, partitionKey, rowKey, jsonSerializerSettings, propertyConverters);
7277
}
7378

7479
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
@@ -80,7 +85,7 @@ public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
8085

8186
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
8287
Expression<Func<T, object>> partitionProperty,
83-
Expression<Func<T, object>> rowProperty, JsonSerializerSettings jsonSerializerSettings) where T : new()
88+
Expression<Func<T, object>> rowProperty, JsonSerializerSettings jsonSerializerSettings, PropertyConverters<T> propertyConverters = null) where T : new()
8489
{
8590
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
8691

@@ -96,7 +101,7 @@ public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
96101
convertRow = r => (TR)(object)Guid.Parse(r);
97102
}
98103
return FromTableEntity(entity, partitionProperty, convertPartition,
99-
rowProperty, convertRow, jsonSerializerSettings);
104+
rowProperty, convertRow, jsonSerializerSettings, propertyConverters);
100105
}
101106

102107
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
@@ -111,7 +116,7 @@ public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
111116
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
112117
Expression<Func<T, object>> partitionProperty,
113118
Func<string, TP> convertPartitionKey, Expression<Func<T, object>> rowProperty,
114-
Func<string, TR> convertRowKey, JsonSerializerSettings jsonSerializerSettings) where T : new()
119+
Func<string, TR> convertRowKey, JsonSerializerSettings jsonSerializerSettings, PropertyConverters<T> propertyConverters = null) where T : new()
115120
{
116121
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
117122

@@ -144,7 +149,7 @@ public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
144149
}
145150

146151
SetTimestamp(entity, o, properties);
147-
FillProperties(entity, o, properties, jsonSerializerSettings);
152+
FillProperties(entity, o, properties, jsonSerializerSettings, propertyConverters);
148153
return o;
149154
}
150155

@@ -153,11 +158,11 @@ public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
153158
return FromTableEntity<T>(entity, _defaultJsonSerializerSettings);
154159
}
155160

156-
public static T FromTableEntity<T>(this DynamicTableEntity entity, JsonSerializerSettings jsonSerializerSettings) where T : new()
161+
public static T FromTableEntity<T>(this DynamicTableEntity entity, JsonSerializerSettings jsonSerializerSettings, PropertyConverters<T> propertyConverters = null) where T : new()
157162
{
158163
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
159164

160-
return entity.FromTableEntity<T, object, object>(null, null, null, null, jsonSerializerSettings);
165+
return entity.FromTableEntity<T, object, object>(null, null, null, null, jsonSerializerSettings, propertyConverters);
161166
}
162167

163168
internal static string GetPropertyNameFromExpression<T>(Expression<Func<T, object>> exp)
@@ -203,11 +208,15 @@ internal static string GetPropertyNameFromExpression<T>(Expression<Func<T, objec
203208
}
204209
}
205210

206-
private static void FillProperties<T>(DynamicTableEntity entity, T o, List<PropertyInfo> properties, JsonSerializerSettings jsonSerializerSettings) where T : new()
211+
private static void FillProperties<T>(DynamicTableEntity entity, T o, List<PropertyInfo> properties, JsonSerializerSettings jsonSerializerSettings, PropertyConverters<T> propertyConverters) where T : new()
207212
{
208213
foreach (var propertyInfo in properties)
209214
{
210-
if (entity.Properties.ContainsKey(propertyInfo.Name) && propertyInfo.Name != nameof(DynamicTableEntity.Timestamp))
215+
if (propertyConverters != null && entity.Properties.ContainsKey(propertyInfo.Name) && propertyConverters.ContainsKey(propertyInfo.Name))
216+
{
217+
propertyConverters[propertyInfo.Name].SetObjectProperty(o, entity.Properties[propertyInfo.Name]);
218+
}
219+
else if (entity.Properties.ContainsKey(propertyInfo.Name) && propertyInfo.Name != nameof(DynamicTableEntity.Timestamp))
211220
{
212221
var val = entity.Properties[propertyInfo.Name].PropertyAsObject;
213222

@@ -250,58 +259,67 @@ internal static string GetPropertyNameFromExpression<T>(Expression<Func<T, objec
250259
}
251260
}
252261

253-
private static DynamicTableEntity CreateTableEntity(object o, List<PropertyInfo> properties,
254-
string partitionKey, string rowKey, JsonSerializerSettings jsonSerializerSettings)
262+
private static DynamicTableEntity CreateTableEntity<T>(object o, List<PropertyInfo> properties,
263+
string partitionKey, string rowKey, JsonSerializerSettings jsonSerializerSettings, PropertyConverters<T> propertyConverters)
255264
{
256265
var entity = new DynamicTableEntity(partitionKey, rowKey);
257266
foreach (var propertyInfo in properties)
258267
{
259268
var name = propertyInfo.Name;
260269
var val = propertyInfo.GetValue(o);
261270
EntityProperty entityProperty;
262-
switch (val)
271+
if (propertyConverters != null && propertyConverters.ContainsKey(name))
263272
{
264-
case int x:
265-
entityProperty = new EntityProperty(x);
266-
break;
267-
case short x:
268-
entityProperty = new EntityProperty(x);
269-
break;
270-
case byte x:
271-
entityProperty = new EntityProperty(x);
272-
break;
273-
case string x:
274-
entityProperty = new EntityProperty(x);
275-
break;
276-
case double x:
277-
entityProperty = new EntityProperty(x);
278-
break;
279-
case DateTime x:
280-
entityProperty = new EntityProperty(x);
281-
break;
282-
case DateTimeOffset x:
283-
entityProperty = new EntityProperty(x);
284-
break;
285-
case bool x:
286-
entityProperty = new EntityProperty(x);
287-
break;
288-
case byte[] x:
289-
entityProperty = new EntityProperty(x);
290-
break;
291-
case long x:
292-
entityProperty = new EntityProperty(x);
293-
break;
294-
case Guid x:
295-
entityProperty = new EntityProperty(x);
296-
break;
297-
case null:
298-
entityProperty = new EntityProperty((int?)null);
299-
break;
300-
default:
301-
name += "Json";
302-
entityProperty = new EntityProperty(JsonConvert.SerializeObject(val, jsonSerializerSettings ?? _defaultJsonSerializerSettings));
303-
break;
273+
entityProperty = propertyConverters[name].ToTableEntityProperty((T)o);
304274
}
275+
else
276+
{
277+
switch (val)
278+
{
279+
case int x:
280+
entityProperty = new EntityProperty(x);
281+
break;
282+
case short x:
283+
entityProperty = new EntityProperty(x);
284+
break;
285+
case byte x:
286+
entityProperty = new EntityProperty(x);
287+
break;
288+
case string x:
289+
entityProperty = new EntityProperty(x);
290+
break;
291+
case double x:
292+
entityProperty = new EntityProperty(x);
293+
break;
294+
case DateTime x:
295+
entityProperty = new EntityProperty(x);
296+
break;
297+
case DateTimeOffset x:
298+
entityProperty = new EntityProperty(x);
299+
break;
300+
case bool x:
301+
entityProperty = new EntityProperty(x);
302+
break;
303+
case byte[] x:
304+
entityProperty = new EntityProperty(x);
305+
break;
306+
case long x:
307+
entityProperty = new EntityProperty(x);
308+
break;
309+
case Guid x:
310+
entityProperty = new EntityProperty(x);
311+
break;
312+
case null:
313+
entityProperty = new EntityProperty((int?)null);
314+
break;
315+
default:
316+
name += "Json";
317+
entityProperty = new EntityProperty(JsonConvert.SerializeObject(val,
318+
jsonSerializerSettings ?? _defaultJsonSerializerSettings));
319+
break;
320+
}
321+
}
322+
305323
entity.Properties[name] = entityProperty;
306324
}
307325
return entity;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.Azure.Cosmos.Table;
4+
5+
namespace TableStorage.Abstractions.TableEntityConverters
6+
{
7+
public class PropertyConverter<T>
8+
{
9+
public Func<T, EntityProperty> ToTableEntityProperty { get; }
10+
public Action<T,EntityProperty> SetObjectProperty { get; }
11+
12+
public PropertyConverter(Func<T, EntityProperty> toTableEntityProperty, Action<T, EntityProperty> setObjectProperty)
13+
{
14+
ToTableEntityProperty = toTableEntityProperty;
15+
SetObjectProperty = setObjectProperty;
16+
}
17+
18+
}
19+
public class PropertyConverters<T> : Dictionary<string, PropertyConverter<T>>
20+
{
21+
22+
}
23+
}

src/TableStorage.Abstractions.TableEntityConverters/TableStorage.Abstractions.TableEntityConverters.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
2323
<AssemblyVersion>1.3.2.0</AssemblyVersion>
2424
<Nullable>disable</Nullable>
25-
<PackageVersion>1.4.1.0</PackageVersion>
25+
<PackageVersion>15.0.0-beta</PackageVersion>
2626
</PropertyGroup>
2727

2828
<ItemGroup>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
namespace TableStorage.Abstractions.UnitTests
4+
{
5+
public class Car
6+
{
7+
public int Year { get; set; }
8+
public string Id { get; set; }
9+
public string Make { get; set; }
10+
public string Model { get; set; }
11+
public DateTime ReleaseDate { get; set; }
12+
}
13+
}

src/TableStorage.Abstractions.UnitTests/EntityConvertTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using Microsoft.Azure.Cosmos.Table;
34
using Newtonsoft.Json;
45
using TableStorage.Abstractions.TableEntityConverters;
56
using Xunit;
@@ -518,5 +519,53 @@ public void convert_to_entity_table_with_explicit_Keys_with_ignored_simple_prope
518519
Assert.False(tableEntity.Properties.ContainsKey("ExternalId"));
519520
Assert.False(tableEntity.Properties.ContainsKey("HireDate"));
520521
}
522+
523+
[Fact]
524+
public void convert_to_entity_table_custom_serialized_property()
525+
{
526+
var car = new Car {
527+
Id = "abc",
528+
Make = "BMW",
529+
Model = "M5",
530+
Year = 2022,
531+
ReleaseDate = new DateTime(2022, 3, 1)
532+
};
533+
534+
var propertyConverters = new PropertyConverters<Car> {
535+
[nameof(car.ReleaseDate)] =
536+
new PropertyConverter<Car>(x =>
537+
new EntityProperty(car.ReleaseDate.ToString("yyyy-M-d")),
538+
(c,p) =>c.ReleaseDate = DateTime.Parse(p.StringValue)
539+
)
540+
};
541+
var carEntity =
542+
car.ToTableEntity(c => c.Year, c => car.Id, new JsonSerializerSettings(), propertyConverters);
543+
Assert.Equal("2022-3-1", carEntity.Properties[nameof(car.ReleaseDate)].StringValue);
544+
}
545+
546+
[Fact]
547+
public void convert_from_entity_table_custom_serialized_property()
548+
{
549+
var car = new Car {
550+
Id = "abc",
551+
Make = "BMW",
552+
Model = "M5",
553+
Year = 2022,
554+
ReleaseDate = new DateTime(2022, 3, 1)
555+
};
556+
557+
var propertyConverters = new PropertyConverters<Car> {
558+
[nameof(car.ReleaseDate)] =
559+
new PropertyConverter<Car>(x =>
560+
new EntityProperty(car.ReleaseDate.ToString("yyyy-M-d")),
561+
(c,p) =>c.ReleaseDate = DateTime.Parse(p.StringValue)
562+
)
563+
};
564+
var carEntity =
565+
car.ToTableEntity(c => c.Year, c => car.Id, new JsonSerializerSettings(), propertyConverters);
566+
567+
var fromEntity = carEntity.FromTableEntity<Car,int,string>(c=>c.Year, c=>c.Id, new JsonSerializerSettings(), propertyConverters);
568+
Assert.Equal(new DateTime(2022, 3, 1), fromEntity.ReleaseDate);
569+
}
521570
}
522571
}

0 commit comments

Comments
 (0)