好的,我现在已经制定了一种方法。我会在这里分享。
问题是你可以去规范化表格,也就是。将实体及其所有嵌套对象和列表属性放入 1 tableentity => 1 行。这意味着复杂的属性或类类型属性需要序列化为字符串(最有可能是 json)。
或者您可以建立关系,其中实体共享相同的分区键。然后批量创建它们。例如。部门 -> 人员。所以一个部门有partionkey = Department01,而个人X 有partionkey = Department01。 2 行。
但是如果你真的想同时做这两个,比如说有不同的行,但是每个表实体也有 IEnumerable 属性,比如列表和集合,如果将它们分成不同的行也将是多余的。
我发现了这个很棒的社区库,我对其进行了扩展并制作了 2 个通用方法。它们并不完美,但这是一个开始。
https://github.com/giometrix/TableStorage.Abstractions.POCO
在这里,您可以轻松地将 POCO 实体转换为 TableEntity,反之亦然。非规范化。
我将这 2 个通用方法添加到库中:
/// <summary>
/// Adds relationship One To Many between source (one) and related entitiy targets (many). Source and related targets have seperate rows but share the same partition key
/// </summary>
/// <typeparam name="TTarget">Target class, ex. Persons</typeparam>
/// <param name="entitySource">Source entity that only has one entry</param>
/// <param name="relatedEntities">Related entities contained in source entity, this can be 0 to many, ex. e => e.Persons</param>
/// <param name="entityTargetRowKey">Target entity rowkey property, needs to be different than source rowkey</param>
/// <returns></returns>
public async Task InsertBatchOneToMany<TTarget>(T entitySource, Expression<Func<T, IEnumerable<TTarget>>> relatedEntities, Expression<Func<TTarget, string>> entityTargetRowKey) where TTarget : class
{
try
{
//TODO: Put related property on ignorelist for json serializing
//Create the batch operation
TableBatchOperation batchOperation = new TableBatchOperation();
IEnumerable<TTarget> targets = relatedEntities.Compile().Invoke(entitySource);
//Insert source entity to batch
DynamicTableEntity source = CreateEntity(entitySource);
batchOperation.InsertOrMerge(source);
//Insert target entities to batch
foreach (var entityTarget in targets)
{
string trowKey = entityTargetRowKey.Compile().Invoke(entityTarget);
batchOperation.InsertOrMerge(entityTarget.ToTableEntity(source.PartitionKey, trowKey));
}
//Execute batch
IList<TableResult> results = await _table.ExecuteBatchAsync(batchOperation);
}
catch (StorageException ex)
{
throw new StorageException($"Error saving data to Table." +
$"{ System.Environment.NewLine}Error Message: {ex.Message}" +
$"{ System.Environment.NewLine}Error Extended Information: {ex.RequestInformation.ExtendedErrorInformation.ErrorMessage}" +
$"{ System.Environment.NewLine}Error Code: {ex.RequestInformation.ExtendedErrorInformation.ErrorCode}");
}
}
/// <summary>
/// Retrieve source and its related target entities back again to source
/// </summary>
/// <typeparam name="TTarget">Related Entity</typeparam>
/// <param name="partitionKey">Partionkey shared by source and related target entities</param>
/// <param name="relatedEntities">Related entities contained in source entity, ex. e => e.Persons</param>
/// <returns></returns>
public async Task<T> GetBatchOneToMany<TTarget>(string partitionKey, Expression<Func<T, IEnumerable<TTarget>>> relatedEntities) where TTarget : class, new()
{
var dynTableEntities = await _tableStore.GetByPartitionKeyAsync(partitionKey);
T convertSource = new T();
TTarget convertTarget = new TTarget();
var targetObjects = new List<TTarget>();
MemberExpression member = relatedEntities.Body as MemberExpression;
PropertyInfo propInfo = member.Member as PropertyInfo;
IEnumerable<TTarget> targets = relatedEntities.Compile().Invoke(convertSource);
bool sourceFound = false;
foreach (var dynTableEntity in dynTableEntities)
{
//Try convert to source
int nonNullValuesSource = 0;
int nonNullValuesTarget = 0;
if (!sourceFound)
{
convertSource = dynTableEntity.FromTableEntity<T>();
nonNullValuesSource = convertSource.GetType().GetProperties().Select(x => x.GetValue(convertSource)).Count(v => v != null);
}
//Try convert to target
convertTarget = dynTableEntity.FromTableEntity<TTarget>();
nonNullValuesTarget = convertTarget.GetType().GetProperties().Select(x => x.GetValue(convertTarget)).Count(v => v != null);
if (nonNullValuesSource > nonNullValuesTarget)
{
sourceFound = true;
}
else
{
targetObjects.Add(convertTarget);
}
}
propInfo.SetValue(convertSource, targetObjects);
return convertSource;
}
这允许我同时建立关系和非规范化行。
用法:
public async Task AddProject(GovernorProject project)
{
//Commit to table
await _repository.InsertBatchOneToMany(project, p => p.Environments, e => e.DisplayName);
}
public async Task<GovernorProject> GetProject(string projectId)
{
return await _repository.GetBatchOneToMany(projectId, p => p.Environments);
}
在我的例子中,我有一个项目的主要实体,每个项目都有 0 个或多个与GovernorProject 的Collection<Environment> Environment 属性相关的环境