Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
Allow PartitonKey and RowKey on the the same property (#1878)
Browse files Browse the repository at this point in the history
  • Loading branch information
chkeita authored May 2, 2022
1 parent a0443fb commit 3541b9a
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 35 deletions.
74 changes: 39 additions & 35 deletions src/ApiService/ApiService/onefuzzlib/orm/EntityConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public enum EntityPropertyKind {
Column
}
public record EntityProperty(string name, string columnName, Type type, EntityPropertyKind kind, (TypeDiscrimnatorAttribute, ITypeProvider)? discriminator);
public record EntityInfo(Type type, Dictionary<string, EntityProperty> properties, Func<object?[], object> constructor);
public record EntityInfo(Type type, ILookup<string, EntityProperty> properties, Func<object?[], object> constructor);

class OnefuzzNamingPolicy : JsonNamingPolicy {
public override string ConvertName(string name) {
Expand Down Expand Up @@ -93,40 +93,44 @@ public static JsonSerializerOptions GetJsonSerializerOptions() {
return ctor;
}

private IEnumerable<EntityProperty> GetEntityProperties<T>(ParameterInfo parameterInfo) {
var name = parameterInfo.Name.EnsureNotNull($"Invalid paramter {parameterInfo}");
var parameterType = parameterInfo.ParameterType.EnsureNotNull($"Invalid paramter {parameterInfo}");
var isRowkey = parameterInfo.GetCustomAttribute(typeof(RowKeyAttribute)) != null;
var isPartitionkey = parameterInfo.GetCustomAttribute(typeof(PartitionKeyAttribute)) != null;

var discriminatorAttribute = typeof(T).GetProperty(name)?.GetCustomAttribute<TypeDiscrimnatorAttribute>();

(TypeDiscrimnatorAttribute, ITypeProvider)? discriminator = null;
if (discriminatorAttribute != null) {
var t = (ITypeProvider)(discriminatorAttribute.ConverterType.GetConstructor(new Type[] { })?.Invoke(null) ?? throw new Exception("unable to retrive the type provider"));
discriminator = (discriminatorAttribute, t);
}


if (isPartitionkey) {
yield return new EntityProperty(name, "PartitionKey", parameterType, EntityPropertyKind.PartitionKey, discriminator);
}

if (isRowkey) {
yield return new EntityProperty(name, "RowKey", parameterType, EntityPropertyKind.RowKey, discriminator);
}

if (!isPartitionkey && !isRowkey) {
var columnName = typeof(T).GetProperty(name)?.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name ?? CaseConverter.PascalToSnake(name);
yield return new EntityProperty(name, columnName, parameterType, EntityPropertyKind.Column, discriminator);
}
}


private EntityInfo GetEntityInfo<T>() {
return _cache.GetOrAdd(typeof(T), type => {
var constructor = type.GetConstructors()[0];
var parameterInfos = constructor.GetParameters();
var parameters =
parameterInfos.Select(f => {
var name = f.Name.EnsureNotNull($"Invalid paramter {f}");
var parameterType = f.ParameterType.EnsureNotNull($"Invalid paramter {f}");
var isRowkey = f.GetCustomAttribute(typeof(RowKeyAttribute)) != null;
var isPartitionkey = f.GetCustomAttribute(typeof(PartitionKeyAttribute)) != null;



var (columnName, kind) =
isRowkey
? ("RowKey", EntityPropertyKind.RowKey)
: isPartitionkey
? ("PartitionKey", EntityPropertyKind.PartitionKey)
: (// JsonPropertyNameAttribute can only be applied to properties
typeof(T).GetProperty(name)?.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name
?? CaseConverter.PascalToSnake(name),
EntityPropertyKind.Column
);
var discriminatorAttribute = type.GetProperty(name)?.GetCustomAttribute<TypeDiscrimnatorAttribute>();

(TypeDiscrimnatorAttribute, ITypeProvider)? discriminator = null;
if (discriminatorAttribute != null) {
var t = (ITypeProvider)(discriminatorAttribute.ConverterType.GetConstructor(new Type[] { })?.Invoke(null) ?? throw new Exception("unable to retrive the type provider"));
discriminator = (discriminatorAttribute, t);
}
return new EntityProperty(name, columnName, parameterType, kind, discriminator);
}).ToArray();
parameterInfos.SelectMany(GetEntityProperties<T>).ToArray();

return new EntityInfo(typeof(T), parameters.ToDictionary(x => x.name), BuildConstructerFrom(constructor));
return new EntityInfo(typeof(T), parameters.ToLookup(x => x.name), BuildConstructerFrom(constructor));
});
}

Expand All @@ -145,8 +149,8 @@ public TableEntity ToTableEntity<T>(T typedEntity) where T : EntityBase {
}
var tableEntity = new TableEntity();
var entityInfo = GetEntityInfo<T>();
foreach (var kvp in entityInfo.properties) {
var prop = kvp.Value;
foreach (var prop in entityInfo.properties.SelectMany(x => x)) {
//var prop = kvp.First();
var value = entityInfo.type.GetProperty(prop.name)?.GetValue(typedEntity);
if (prop.kind == EntityPropertyKind.PartitionKey || prop.kind == EntityPropertyKind.RowKey) {
tableEntity.Add(prop.columnName, value?.ToString());
Expand Down Expand Up @@ -190,7 +194,7 @@ public TableEntity ToTableEntity<T>(T typedEntity) where T : EntityBase {


private object? GetFieldValue(EntityInfo info, string name, TableEntity entity) {
var ef = info.properties[name];
var ef = info.properties[name].First();
if (ef.kind == EntityPropertyKind.PartitionKey || ef.kind == EntityPropertyKind.RowKey) {
if (ef.type == typeof(string))
return entity.GetString(ef.kind.ToString());
Expand Down Expand Up @@ -259,7 +263,7 @@ public TableEntity ToTableEntity<T>(T typedEntity) where T : EntityBase {
public T ToRecord<T>(TableEntity entity) where T : EntityBase {
var entityInfo = GetEntityInfo<T>();
var parameters =
entityInfo.properties.Keys.Select(k => GetFieldValue(entityInfo, k, entity)).ToArray();
entityInfo.properties.Select(grouping => GetFieldValue(entityInfo, grouping.Key, entity)).ToArray();
try {
var entityRecord = (T)entityInfo.constructor.Invoke(parameters);
if (entity.ETag != _emptyETag) {
Expand All @@ -271,8 +275,8 @@ public T ToRecord<T>(TableEntity entity) where T : EntityBase {
} catch (Exception) {
var stringParam = string.Join(", ", parameters);
throw new Exception($"Could not initialize object of type {typeof(T)} with the following parameters: {stringParam} constructor {entityInfo.constructor}");
}

}

}

}
Expand Down
24 changes: 24 additions & 0 deletions src/ApiService/Tests/OrmTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,5 +279,29 @@ public void TestContainerSerialization2() {
Assert.Equal("abc", entity?.TheName);
Assert.Equal("abc-123", entity?.Container.ContainerName);
}


record Entity4(
[RowKey][PartitionKey] int Id,
string TheName,
Container Container
) : EntityBase();

[Fact]
public void TestPartitionKeyIsRowKey() {
var container = new Container("abc-123");
var expected = new Entity4(123, "abc", container);
var converter = new EntityConverter();

var tableEntity = converter.ToTableEntity(expected);
Assert.Equal(expected.Id.ToString(), tableEntity.RowKey);
Assert.Equal(expected.Id.ToString(), tableEntity.PartitionKey);

var actual = converter.ToRecord<Entity4>(tableEntity);

Assert.Equal(expected.Container.ContainerName, actual.Container.ContainerName);
Assert.Equal(expected.Container.ContainerName, tableEntity.GetString("container"));
}

}
}

0 comments on commit 3541b9a

Please sign in to comment.