private static Func CreateTableEntitySerializer()

in src/DurableTask.AzureStorage/Tracking/TableEntityConverter.cs [142:213]


        private static Func<object, TableEntity> CreateTableEntitySerializer(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            Debug.Assert(type.GetCustomAttribute<DataContractAttribute>() != null);

            // Indexers use "get_Item" and "set_Item"
            MethodInfo setItemMethod = typeof(TableEntity).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) });
            MethodInfo serializeMethod = typeof(Utils).GetMethod(nameof(Utils.SerializeToJson), new Type[] { typeof(string) });

            ParameterExpression objParam = Expression.Parameter(typeof(object), "obj");
            ParameterExpression inputVar = Expression.Variable(type, "input");
            ParameterExpression tableVar = Expression.Variable(typeof(TableEntity), "table");
            var variables = new List<ParameterExpression> { inputVar, tableVar };
            var body = new List<Expression>();

            #region if (obj == null) throw new ArgumentNullException(nameof(obj));
            body.Add(Expression.IfThen(
                Expression.Equal(objParam, Expression.Constant(null, typeof(object))),
                Expression.Throw(
                    Expression.New(
                        typeof(ArgumentNullException).GetConstructor(new Type[] { typeof(string) }),
                        Expression.Constant(objParam.Name, typeof(string))))));
            #endregion

            #region <Type> input = (<Type>)obj;
            body.Add(Expression.Assign(inputVar, type != typeof(object) ? Expression.Convert(objParam, type) : objParam));
            #endregion

            #region TableEntity table = new TableEntity();
            body.Add(Expression.Assign(tableVar, Expression.New(typeof(TableEntity))));
            #endregion

            foreach ((string propertyName, Type memberType, MemberInfo metadata) in EnumerateMembers(type))
            {
                #region table["<property>"] = (object)/* ... */
                Expression valueExpr;
                MemberExpression memberExpr = Expression.MakeMemberAccess(inputVar, metadata);
                if (memberType.IsEnum)
                {
                    #region table["<property>"] = input.<Member>.ToString("G");
                    MethodInfo toStringMethod = memberType.GetMethod(nameof(object.ToString), new Type[] { typeof(string) });
                    valueExpr = Expression.Call(memberExpr, toStringMethod, Expression.Constant("G", typeof(string)));
                    #endregion
                }
                else if (IsSupportedType(memberType))
                {
                    #region table["<property>"] = input.<Member>;
                    valueExpr = memberExpr;
                    #endregion
                }
                else
                {
                    // Note: We also serialize nullable enumerations using JSON for backwards compatibility
                    #region table["<property>"] = Utils.SerializeToJson(input.<Member>);
                    valueExpr = Expression.Call(null, serializeMethod, memberType != typeof(object) ? Expression.Convert(memberExpr, typeof(object)) : memberExpr);
                    #endregion
                }

                body.Add(Expression.Call(tableVar, setItemMethod, Expression.Constant(propertyName, typeof(string)), Expression.Convert(valueExpr, typeof(object))));
                #endregion
            }

            #region return table;
            body.Add(tableVar);
            #endregion

            return Expression.Lambda<Func<object, TableEntity>>(Expression.Block(variables, body), objParam).Compile();
        }