Skip to content

Commit

Permalink
Move ToEntity to the entity properties mapper
Browse files Browse the repository at this point in the history
This makes the code more cohesive, having a single place where Table Entity to/from TEntity is performed.
  • Loading branch information
kzu committed Mar 22, 2024
1 parent 5324102 commit 441e232
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 70 deletions.
70 changes: 70 additions & 0 deletions src/TableStorage/EntityPropertiesMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using Azure.Data.Tables;

namespace Devlooped
Expand Down Expand Up @@ -88,5 +90,73 @@ pair.Value is DateTimeOffset dateOffset ?

return (IDictionary<string, object>)values;
}

/// <summary>
/// Uses JSON deserialization to convert from the persisted entity data
/// to the entity type, so that the right constructor and property
/// setters can be invoked, even if they are internal/private.
/// </summary>
public T ToEntity<T>(TableEntity data, string? partitionKeyProperty = default, string? rowKeyProperty = default)
{
using var mem = new MemoryStream();
using var writer = new Utf8JsonWriter(mem);

// Write entity properties in json format so deserializer can
// perform its advanced ctor and conversion detection as usual.
writer.WriteStartObject();

if (partitionKeyProperty != null && !data.ContainsKey(partitionKeyProperty))
writer.WriteString(partitionKeyProperty, data.PartitionKey);

if (rowKeyProperty != null && !data.ContainsKey(rowKeyProperty))
writer.WriteString(rowKeyProperty, data.RowKey);

if (data.Timestamp != null && !data.ContainsKey(nameof(ITableEntity.Timestamp)))
writer.WriteString(nameof(ITableEntity.Timestamp), data.Timestamp.Value.ToString("O"));

foreach (var property in data)
{
switch (property.Value)
{
case string value:
writer.WriteString(property.Key, value);
break;
case byte[] value:
writer.WriteBase64String(property.Key, value);
break;
case bool value:
writer.WriteBoolean(property.Key, value);
break;
case DateTime value:
writer.WriteString(property.Key, value);
break;
case DateTimeOffset value:
writer.WriteString(property.Key, value);
break;
case double value:
writer.WriteNumber(property.Key, value);
break;
case int value:
writer.WriteNumber(property.Key, value);
break;
case long value:
writer.WriteNumber(property.Key, value);
break;
case Guid value:
writer.WriteString(property.Key, value);
break;
default:
break;
}
}

writer.WriteEndObject();
writer.Flush();
mem.Position = 0;

var json = new StreamReader(mem).ReadToEnd();

return DocumentSerializer.Default.Deserialize<T>(json)!;
}
}
}
72 changes: 2 additions & 70 deletions src/TableStorage/TableRepository`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public async IAsyncEnumerable<T> EnumerateAsync(string? partitionKey = default,

await foreach (var entity in table.QueryAsync<TableEntity>(filter, cancellationToken: cancellation).WithCancellation(cancellation))
{
yield return ToEntity(entity);
yield return EntityPropertiesMapper.Default.ToEntity<T>(entity, partitionKeyProperty, rowKeyProperty);
}
}

Expand All @@ -166,7 +166,7 @@ public async IAsyncEnumerable<T> EnumerateAsync(string? partitionKey = default,
var result = await table.GetEntityAsync<TableEntity>(partitionKey, rowKey, cancellationToken: cancellation)
.ConfigureAwait(false);

return ToEntity(result.Value);
return EntityPropertiesMapper.Default.ToEntity<T>(result.Value, partitionKeyProperty, rowKeyProperty);
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Expand All @@ -192,73 +192,5 @@ public async Task<T> PutAsync(T entity, CancellationToken cancellation = default

return (await GetAsync(partitionKey, rowKey, cancellation).ConfigureAwait(false))!;
}

/// <summary>
/// Uses JSON deserialization to convert from the persisted entity data
/// to the entity type, so that the right constructor and property
/// setters can be invoked, even if they are internal/private.
/// </summary>
T ToEntity(TableEntity entity)
{
using var mem = new MemoryStream();
using var writer = new Utf8JsonWriter(mem);

// Write entity properties in json format so deserializer can
// perform its advanced ctor and conversion detection as usual.
writer.WriteStartObject();

if (partitionKeyProperty != null && !entity.ContainsKey(partitionKeyProperty))
writer.WriteString(partitionKeyProperty, entity.PartitionKey);

if (rowKeyProperty != null && !entity.ContainsKey(rowKeyProperty))
writer.WriteString(rowKeyProperty, entity.RowKey);

if (entity.Timestamp != null && !entity.ContainsKey(nameof(ITableEntity.Timestamp)))
writer.WriteString(nameof(ITableEntity.Timestamp), entity.Timestamp.Value.ToString("O"));

foreach (var property in entity)
{
switch (property.Value)
{
case string value:
writer.WriteString(property.Key, value);
break;
case byte[] value:
writer.WriteBase64String(property.Key, value);
break;
case bool value:
writer.WriteBoolean(property.Key, value);
break;
case DateTime value:
writer.WriteString(property.Key, value);
break;
case DateTimeOffset value:
writer.WriteString(property.Key, value);
break;
case double value:
writer.WriteNumber(property.Key, value);
break;
case int value:
writer.WriteNumber(property.Key, value);
break;
case long value:
writer.WriteNumber(property.Key, value);
break;
case Guid value:
writer.WriteString(property.Key, value);
break;
default:
break;
}
}

writer.WriteEndObject();
writer.Flush();
mem.Position = 0;

var json = new StreamReader(mem).ReadToEnd();

return serializer.Deserialize<T>(json)!;
}
}
}

0 comments on commit 441e232

Please sign in to comment.