in src/DurableTask.AzureStorage/Tracking/TableEntityConverter.cs [37:140]
public static TableEntity Serialize(object obj) =>
SerializerCache.GetOrAdd(obj?.GetType(), t => CreateTableEntitySerializer(t)).Invoke(obj);
public static object Deserialize(TableEntity entity, Type type) =>
DeserializeCache.GetOrAdd(type, t => CreateTableEntityDeserializer(t)).Invoke(entity);
private static Func<TableEntity, object> CreateTableEntityDeserializer(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
Debug.Assert(type.GetCustomAttribute<DataContractAttribute>() != null);
MethodInfo getStringMethod = typeof(TableEntity).GetMethod(nameof(TableEntity.GetString), new Type[] { typeof(string) });
MethodInfo parseMethod = typeof(Enum).GetMethod(nameof(Enum.Parse), new Type[] { typeof(Type), typeof(string) });
MethodInfo deserializeMethod = typeof(Utils).GetMethod(nameof(Utils.DeserializeFromJson), new Type[] { typeof(string) });
ParameterExpression tableParam = Expression.Variable(typeof(TableEntity), "table");
ParameterExpression outputVar = Expression.Parameter(type, "output");
var variables = new List<ParameterExpression> { outputVar };
var body = new List<Expression>();
#region if (table == null) throw new ArgumentNullException(nameof(table));
body.Add(Expression.IfThen(
Expression.Equal(tableParam, Expression.Constant(null, typeof(TableEntity))),
Expression.Throw(
Expression.New(
typeof(ArgumentNullException).GetConstructor(new Type[] { typeof(string) }),
Expression.Constant(tableParam.Name, typeof(string))))));
#endregion
#region <Type> output = (<Type>)FormatterServices.GetUninitializedObject(typeof(<Type>));
MethodInfo getUninitializedObjectMethod = typeof(FormatterServices).GetMethod(nameof(FormatterServices.GetUninitializedObject), new Type[] { typeof(Type) });
body.Add(Expression.Assign(
outputVar,
Expression.Convert(
Expression.Call(null, getUninitializedObjectMethod, Expression.Constant(type, typeof(Type))),
type)));
#endregion
foreach ((string propertyName, Type memberType, MemberInfo metadata) in EnumerateMembers(type))
{
#region output.<Member> = /* ... */
Expression valueExpr;
if (memberType.IsEnum)
{
#region string <Member>Variable = table.GetString("<property>");
ParameterExpression enumStringVar = Expression.Parameter(typeof(string), propertyName + "Variable");
variables.Add(enumStringVar);
body.Add(Expression.Assign(enumStringVar, Expression.Call(tableParam, getStringMethod, Expression.Constant(propertyName, typeof(string)))));
#endregion
#region output.<Member> = <Member>Variable == null ? default(<MemberType>) : (<MemberType>)Enum.Parse(typeof(<MemberType>), <Member>Variable);
valueExpr = Expression.Condition(
Expression.Equal(enumStringVar, Expression.Constant(null, typeof(string))),
Expression.Default(memberType),
Expression.Convert(Expression.Call(null, parseMethod, Expression.Constant(memberType, typeof(Type)), enumStringVar), memberType));
#endregion
}
else if(IsSupportedType(memberType))
{
#region output.<Member> = table.Get<MemberType>("<property>");
MethodInfo accessorMethod = GetEntityAccessor(memberType);
valueExpr = Expression.Call(tableParam, accessorMethod, Expression.Constant(propertyName, typeof(string)));
#endregion
if (memberType.IsValueType && !memberType.IsGenericType) // Cannot be null
{
#region output.<Member> = table.Get<MemberType>("<property>").GetValueOrDefault();
valueExpr = Expression.Call(valueExpr, typeof(Nullable<>).MakeGenericType(memberType).GetMethod(nameof(Nullable<int>.GetValueOrDefault), Type.EmptyTypes));
#endregion
}
}
else
{
// Note: We also deserialize nullable enumerations using JSON for backwards compatibility
#region string <Member>Variable = table.GetString("<property>");
ParameterExpression jsonVariable = Expression.Parameter(typeof(string), propertyName + "Variable");
variables.Add(jsonVariable);
body.Add(Expression.Assign(jsonVariable, Expression.Call(tableParam, getStringMethod, Expression.Constant(propertyName, typeof(string)))));
#endregion
#region output.<Member> = = <Member>Variable == null ? default(<MemberType>) : Utils.DeserializeFromJson<<MemberType>>(<Member>Variable);
valueExpr = Expression.Condition(
Expression.Equal(jsonVariable, Expression.Constant(null, typeof(string))),
Expression.Default(memberType),
Expression.Call(null, deserializeMethod.MakeGenericMethod(memberType), jsonVariable));
#endregion
}
body.Add(Expression.Assign(Expression.MakeMemberAccess(outputVar, metadata), valueExpr));
#endregion
}
#region return (object)output;
body.Add(type != typeof(object) ? Expression.Convert(outputVar, type) : outputVar);
#endregion
return Expression.Lambda<Func<TableEntity, object>>(Expression.Block(variables, body), tableParam).Compile();
}