Skip to content

Commit

Permalink
Merge pull request #45 from Evodim/feat--entity-table-allow-custom-se…
Browse files Browse the repository at this point in the history
…rializer-options

Feat  entity table allow custom serializer options
  • Loading branch information
medevod authored Mar 16, 2023
2 parents 42c775f + 60003e6 commit 377d947
Show file tree
Hide file tree
Showing 16 changed files with 268 additions and 102 deletions.
2 changes: 1 addition & 1 deletion samples/Common.Samples/Fakers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static Faker<PersonEntity> CreateFakePerson(string[] accounts = null)

//A nullable int? with 80% probability of being null.
//The .OrNull extension is in the Bogus.Extensions namespace.
.RuleFor(p => p.OtherAddress, f => FakedAddress().Generate(5))
.RuleFor(p => p.OtherAddresses, f => FakedAddress().Generate(5))
.RuleFor(p => p.LocalCreated, f => f.Date.Between(DateTime.UtcNow.AddYears(-4), DateTime.UtcNow))
.RuleFor(p => p.LocalUpdated, f => f.Date.Between(DateTime.UtcNow.AddYears(-4), DateTime.UtcNow))
.RuleFor(p => p.Created, (f, a) => new DateTimeOffset(a.LocalCreated.Value))
Expand Down
4 changes: 2 additions & 2 deletions samples/Common.Samples/Models/AdressType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
public enum AdressType
{
Billing,
Home
Billing = 0,
Home = 1
}
}
2 changes: 1 addition & 1 deletion samples/Common.Samples/Models/PersonEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class PersonEntity
public DateTime LocalUpdated { get; set; }
public bool? Enabled { get; set; }
public Address Address { get; set; }
public List<Address> OtherAddress { get; set; }
public List<Address> OtherAddresses { get; set; }
public Guid PersonId { get; set; }
public string FirstName { get; set; }
public int? Rank { get; set; }
Expand Down
3 changes: 1 addition & 2 deletions samples/TableClient.Basic.Sample/SampleConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ public static async Task Run()
config
.SetPartitionKey(entity => entity.TenantId)
.SetRowKeyProp(entity => entity.PersonId)
.IgnoreProp(entity => entity.OtherAddress)

.IgnoreProp(entity => entity.OtherAddresses)
//add computed props to store and compute dynamically additional fields of the entity
.AddComputedProp("_IsInFrance", p => p.Address?.State == "France")
.AddComputedProp("_FirstLastName3Chars", p => p.LastName?.ToLower()[..3]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
.ConfigureEntity((sp, config) => config
.SetPartitionKey(p => p.TenantId)
.SetRowKeyProp(p => p.PersonId)
.IgnoreProp(p => p.OtherAddress)
.IgnoreProp(p => p.OtherAddresses)
.AddComputedProp("_IsInFrance", p => p.Address?.State == "France")
.AddComputedProp("_MoreThanOneAddress", p => p.OtherAddress?.Count > 1)
.AddComputedProp("_MoreThanOneAddress", p => p.OtherAddresses?.Count > 1)
.AddObserver("LastNameProjection", () => sp.GetService<SampleProjectionObserver>())
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
.ConfigureEntity((sp, config) => config
.SetPartitionKey(entity => entity.TenantId)
.SetRowKeyProp(entity => entity.PersonId)
.IgnoreProp(entity => entity.OtherAddress)
.IgnoreProp(entity => entity.OtherAddresses)
.AddComputedProp("_IsInFrance", entity => entity.Address?.State == "France")
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddress?.Count > 1)
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddresses?.Count > 1)
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
.SetPartitionKey(entity => entity.TenantId)
.SetRowKeyProp(entity => entity.PersonId)
.AddTag(entity => entity.LastName)
.IgnoreProp(entity => entity.OtherAddress)
.IgnoreProp(entity => entity.OtherAddresses)
.AddComputedProp("_IsInFrance", entity => entity.Address?.State == "France")
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddress?.Count > 1)
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddresses?.Count > 1)
))
.WithName("Source");
});
Expand Down
5 changes: 2 additions & 3 deletions samples/TableClient.Legacy.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
.ConfigureEntity(entityConfig => entityConfig
.SetPartitionKey(entity => entity.TenantId)
.SetRowKeyProp(entity => entity.PersonId)

.IgnoreProp(entity => entity.OtherAddress)
.IgnoreProp(entity => entity.OtherAddresses)

.AddComputedProp("_IsInFrance", entity => entity.Address?.State == "France")
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddress?.Count > 1)
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddresses?.Count > 1)
.AddComputedProp("_CreatedNext6Month", entity => entity.Created > DateTimeOffset.UtcNow.AddMonths(-6))
.AddComputedProp("_FirstLastName3Chars", entity => entity.LastName?.ToLower()[..3])

Expand Down
4 changes: 2 additions & 2 deletions samples/TableClient.Performance.Sample/SampleConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static async Task Run()
config
.SetPartitionKey(entity => entity.TenantId)
.SetRowKeyProp(entity => entity.PersonId)
.IgnoreProp(entity => entity.OtherAddress)
.IgnoreProp(entity => entity.OtherAddresses)

//add tag to generate indexed and sorted entities through rowKey
.AddTag(entity => entity.Created)
Expand All @@ -42,7 +42,7 @@ public static async Task Run()

//add computed props to store and compute dynamically additional fields of the entity
.AddComputedProp("_IsInFrance", entity => entity.Address?.State == "France")
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddress?.Count > 1)
.AddComputedProp("_MoreThanOneAddress", entity => entity.OtherAddresses?.Count > 1)
.AddComputedProp("_CreatedNext6Month", entity => entity.Created > DateTimeOffset.UtcNow.AddMonths(-6))
.AddComputedProp("_FirstLastName3Chars", entity => entity.LastName?.ToLower()[..3])

Expand Down
18 changes: 13 additions & 5 deletions src/Azure.EntityServices.Tables/Core/EntityValueAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ public static class EntityValueAdapter
/// <param name="value"></param>
/// <param name="entityProp"></param>
/// <returns></returns>
public static object WriteValue(object value, PropertyInfo entityProp = null)
public static object WriteValue(
object value,
JsonSerializerOptions serializerOptions,
PropertyInfo entityProp = null
)
{
if (value == null)
{
Expand Down Expand Up @@ -49,7 +53,7 @@ public static object WriteValue(object value, PropertyInfo entityProp = null)
//otherwise try to serialize in string
decimal v => v.ToInvariantString(),
float v => v.ToInvariantString(),
_ => JsonSerializer.Serialize(value)
_ => JsonSerializer.Serialize(value, serializerOptions)
};
}

Expand All @@ -59,7 +63,11 @@ public static object WriteValue(object value, PropertyInfo entityProp = null)
/// <param name="value"></param>
/// <param name="entityProp"></param>
/// <returns></returns>
public static void ReadValue<T>(T entity, PropertyInfo entityProp, object tablePropValue)
public static void ReadValue<T>(
T entity
, PropertyInfo entityProp,
JsonSerializerOptions serializerOptions,
object tablePropValue = null)
{
var propertyType = entityProp.PropertyType;

Expand Down Expand Up @@ -213,10 +221,10 @@ public static void ReadValue<T>(T entity, PropertyInfo entityProp, object tableP
if (propertyType.IsClass && !propertyType.IsValueType)
{
//otherwise it should be a serialized object
entityProp.SetValue(entity, JsonSerializer.Deserialize(strPropValue, propertyType), null);
entityProp.SetValue(entity, JsonSerializer.Deserialize(strPropValue, propertyType, serializerOptions), null);
return;
}
entityProp.SetValue(entity, JsonSerializer.Deserialize(strPropValue, propertyType), null);
entityProp.SetValue(entity, JsonSerializer.Deserialize(strPropValue, propertyType, serializerOptions), null);
return;
}

Expand Down
62 changes: 31 additions & 31 deletions src/Azure.EntityServices.Tables/Core/TableEntityBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
using Azure.EntityServices.Tables.Extensions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text.Json;

namespace Azure.EntityServices.Tables.Core
{
Expand All @@ -19,6 +19,8 @@ namespace Azure.EntityServices.Tables.Core

public TableEntity TableEntity { get; }

private readonly JsonSerializerOptions _serializerOptions;

public T Entity { get; private set; }
public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>();
public IDictionary<string, object> Metadata { get; } = new Dictionary<string, object>();
Expand All @@ -28,61 +30,63 @@ namespace Azure.EntityServices.Tables.Core
BindingFlags.Instance |
BindingFlags.SetProperty);


private readonly IEnumerable<PropertyInfo> _filteredEntityProperties = EntityProperties.ToList();
private readonly EntityTagBuilder<T> _entityTagBuilder;
private readonly IEnumerable<string> _propsToIgnore= Enumerable.Empty<string>();
private readonly IEnumerable<string> _propsToIgnore = Enumerable.Empty<string>();

private List<PropertyInfo> FilterEntityProperties() => EntityProperties.Where(p => !_propsToIgnore.Contains(p.Name)).ToList();

public TableEntityBinder(T entity)
public TableEntityBinder(T entity, JsonSerializerOptions serializerOptions = null)
{
Entity = entity;
TableEntity = new TableEntity();
_serializerOptions = serializerOptions;
}

public TableEntityBinder(T entity, IEnumerable<string> propsToIgnore)
public TableEntityBinder(T entity, IEnumerable<string> propsToIgnore, JsonSerializerOptions serializerOptions = null)
{
Entity = entity;
TableEntity = new TableEntity();
_propsToIgnore = propsToIgnore ?? Enumerable.Empty<string>();
_filteredEntityProperties = FilterEntityProperties();
_propsToIgnore = propsToIgnore.ToList();
_serializerOptions = serializerOptions;
}

public TableEntityBinder(TableEntity tableEntity)
public TableEntityBinder(TableEntity tableEntity, JsonSerializerOptions serializerOptions = null)
{
TableEntity = tableEntity;
_serializerOptions = serializerOptions;
}

public TableEntityBinder(TableEntity tableEntity, IEnumerable<string> propsToIgnore, EntityTagBuilder<T> entityTagBuilder)
public TableEntityBinder(TableEntity tableEntity, IEnumerable<string> propsToIgnore, EntityTagBuilder<T> entityTagBuilder, JsonSerializerOptions serializerOptions = null)
{
TableEntity = tableEntity;
TableEntity = tableEntity;
_propsToIgnore = propsToIgnore ?? Enumerable.Empty<string>();
_filteredEntityProperties = FilterEntityProperties();
_entityTagBuilder = entityTagBuilder;
_serializerOptions = serializerOptions;
}


public TableEntityBinder(T entity, string partitionKey, string rowKey, IEnumerable<string> propsToIgnore=null, EntityTagBuilder<T> entityTagBuilder=null)
public TableEntityBinder(T entity, string partitionKey, string rowKey, IEnumerable<string> propsToIgnore = null, EntityTagBuilder<T> entityTagBuilder = null, JsonSerializerOptions serializerOptions = null)
{
Entity = entity;
TableEntity = new TableEntity(partitionKey, rowKey);
_propsToIgnore = propsToIgnore ?? Enumerable.Empty<string>() ;
_filteredEntityProperties = FilterEntityProperties();
_propsToIgnore = propsToIgnore ?? Enumerable.Empty<string>();
_filteredEntityProperties = FilterEntityProperties();
_entityTagBuilder = entityTagBuilder;
_serializerOptions = serializerOptions;
}

public TableEntity Bind()
{

foreach (var metadata in Metadata)
{
TableEntity.AddOrUpdate(metadata.Key, EntityValueAdapter.WriteValue(metadata.Value));
TableEntity.AddOrUpdate(metadata.Key, EntityValueAdapter.WriteValue(metadata.Value, _serializerOptions));
}
if (Entity is TableEntity tbe)
{
foreach (var property in tbe.Where(e=> !_propsToIgnore.Contains(e.Key)))
foreach (var property in tbe.Where(e => !_propsToIgnore.Contains(e.Key)))
{
if (property.Key == "PartitionKey" ||
property.Key == "RowKey" ||
Expand All @@ -91,20 +95,19 @@ public TableEntity Bind()
{
continue;
}
TableEntity.AddOrUpdate(property.Key,property.Value);
TableEntity.AddOrUpdate(property.Key, property.Value);
}
}
else
{
foreach (var property in _filteredEntityProperties)
{

TableEntity.AddOrUpdate(property.Name, EntityValueAdapter.WriteValue(property.GetValue(Entity), property));
TableEntity.AddOrUpdate(property.Name, EntityValueAdapter.WriteValue(property.GetValue(Entity), _serializerOptions, property));
}
}
foreach (var property in Properties)
{
TableEntity.AddOrUpdate(property.Key, EntityValueAdapter.WriteValue(property.Value));
TableEntity.AddOrUpdate(property.Key, EntityValueAdapter.WriteValue(property.Value, _serializerOptions));
}
return TableEntity;
}
Expand All @@ -128,23 +131,21 @@ public T UnBind()
{
foreach (var property in TableEntity.Keys)
{

tbe.AddOrUpdate(property, TableEntity[property]);
}

}
else
foreach (var property in _filteredEntityProperties)
{
if (TableEntity.TryGetValue(property.Name, out var tablePropValue))
else
foreach (var property in _filteredEntityProperties)
{
EntityValueAdapter.ReadValue(Entity, property, tablePropValue);
if (TableEntity.TryGetValue(property.Name, out var tablePropValue))
{
EntityValueAdapter.ReadValue(Entity, property, _serializerOptions, tablePropValue);
}
}
}

return Entity;
}

public void BindDynamicProps(IDictionary<string, Func<T, object>> props, bool toDelete = false)
{
foreach (var prop in props)
Expand All @@ -159,15 +160,14 @@ public void BindDynamicProps(IDictionary<string, Func<T, object>> props, bool to
}

public void BindTags(Dictionary<string, PropertyInfo> tags, IList<string> computedTags)
{

{
foreach (var propInfo in tags)
{
Metadata.AddOrUpdate(_entityTagBuilder.CreateTagName(propInfo.Key), _entityTagBuilder.CreateTagRowKey(propInfo.Value, Entity));
}
foreach (var tagPrefix in computedTags)
{
Metadata.AddOrUpdate(_entityTagBuilder.CreateTagName(tagPrefix), _entityTagBuilder.CreateTagRowKey(tagPrefix, Metadata[$"{tagPrefix}"], Entity));
Metadata.AddOrUpdate(_entityTagBuilder.CreateTagName(tagPrefix), _entityTagBuilder.CreateTagRowKey(tagPrefix, Metadata[$"{tagPrefix}"], Entity));
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Azure.EntityServices.Tables/EntityTableClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public class EntityTableClient<T> : IEntityTableClient<T>
private EntityTagBuilder<T> _entityTagBuilder;

private IEntityBinder<T> CreatePrimaryEntityBinderFromEntity(T entity)
=> new TableEntityBinder<T>(entity, ResolvePartitionKey(entity), ResolvePrimaryKey(entity), _config.IgnoredProps, _entityTagBuilder);
=> new TableEntityBinder<T>(entity, ResolvePartitionKey(entity), ResolvePrimaryKey(entity), _config.IgnoredProps, _entityTagBuilder, _options.SerializerOptions);

private IEntityBinder<T> CreateEntityBinderFromTableEntity(TableEntity tableEntity)
=> new TableEntityBinder<T>(tableEntity, _config.IgnoredProps, _entityTagBuilder);
=> new TableEntityBinder<T>(tableEntity, _config.IgnoredProps, _entityTagBuilder, _options.SerializerOptions);

private TableBatchClient CreateTableBatchClient()
{
Expand Down
9 changes: 7 additions & 2 deletions src/Azure.EntityServices.Tables/EntityTableClientOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Azure.EntityServices.Tables
using System.Text.Json;

namespace Azure.EntityServices.Tables
{
public class EntityTableClientOptions
{
Expand All @@ -12,7 +14,8 @@ public EntityTableClientOptions(
int maxItemToGroup = 1000,
int maxItemInTransaction = 100,
bool createTableIfNotExists = false,
bool handleTagMutation = false
bool handleTagMutation = false,
JsonSerializerOptions serializerOptions = default
)
{
TableName = tableName;
Expand All @@ -21,6 +24,7 @@ public EntityTableClientOptions(
MaxItemToGroup = maxItemToGroup;
CreateTableIfNotExists = createTableIfNotExists;
HandleTagMutation = handleTagMutation;
SerializerOptions = serializerOptions ?? new JsonSerializerOptions();
}

public bool CreateTableIfNotExists { get; set; }
Expand All @@ -29,5 +33,6 @@ public EntityTableClientOptions(
public int MaxOperationPerTransaction { get; set; } = 100;
public int MaxItemToGroup { get; set; } = 1000;
public bool HandleTagMutation { get; set; }
public JsonSerializerOptions SerializerOptions { get; set; } = new();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Text.Json;

namespace Azure.EntityServices.Tables
{
public static class EntityTableclientOptionExtensions
{
public static EntityTableClientOptions ConfigureSerializer(this EntityTableClientOptions options,Action<JsonSerializerOptions> serializerConfigurator)
{
serializerConfigurator?.Invoke(options.SerializerOptions);
return options;
}

public static EntityTableClientOptions ConfigureSerializerWithStringEnumConverter(this EntityTableClientOptions options)
{
options?.SerializerOptions?.AddJsonStringEnumConverter();
return options;
}
}
}
Loading

0 comments on commit 377d947

Please sign in to comment.