private Action <object, object> GenerateSetter(MaterializerPathInfo pathInfo)
        {
            var targetParameter   = Expression.Parameter(typeof(object));
            var valueParameter    = Expression.Parameter(typeof(object));
            var currentExpression = (Expression)targetParameter;
            var lastMember        = default(MemberInfo);

            for (var i = 0; i < pathInfo.Path.Length; i++)
            {
                var member = pathInfo.Path[i];

                if (!member.DeclaringType.IsAssignableFrom(currentExpression.Type))
                {
                    currentExpression = Expression.Convert(currentExpression, member.DeclaringType);
                }

                if (i + 1 == pathInfo.Path.Length)
                {
                    currentExpression = Expression.MakeMemberAccess(currentExpression, member = GetMemberForWrite(member));
                }
                else
                {
                    currentExpression = Expression.MakeMemberAccess(currentExpression, member = GetMemberForRead(member));
                }

                lastMember = member;
            }

            Expression body = default;

            if (lastMember is FieldInfo field && field.IsInitOnly)
            {
                body
                    = Expression.Call(
                          Expression.Constant(field),
                          typeof(FieldInfo).GetRuntimeMethod(nameof(FieldInfo.SetValue), new[] { typeof(object), typeof(object) }),
                          (currentExpression as MemberExpression).Expression,
                          Expression.Convert(valueParameter, currentExpression.Type));
            }
        private Func <object, object> GenerateGetter(MaterializerPathInfo pathInfo)
        {
            var blockVariables         = new List <ParameterExpression>();
            var blockExpressions       = new List <Expression>();
            var parameter              = Expression.Parameter(typeof(object));
            var currentExpression      = (Expression)parameter;
            var returnLabel            = Expression.Label(typeof(object), "Return");
            var nullConstantExpression = Expression.Constant(null, typeof(object));

            for (var i = 0; i < pathInfo.Path.Length; i++)
            {
                var member = pathInfo.Path[i];

                var memberType = member.GetMemberType();

                var memberVariable = Expression.Variable(memberType, member.Name);

                blockVariables.Add(memberVariable);

                /*if (member.DeclaringType.IsGenericType(typeof(ExpandedGrouping<,>)))
                 * {
                 *  Debug.Assert(member == pathInfo.Path.Last());
                 *
                 *  if (member.Name == "Elements")
                 *  {
                 *      blockExpressions.Add(
                 *          Expression.IfThen(
                 *              Expression.Not(Expression.TypeIs(currentExpression, member.DeclaringType)),
                 *              Expression.Return(returnLabel, currentExpression)));
                 *  }
                 *  else
                 *  {
                 *      member = member.DeclaringType.FindGenericType(typeof(IGrouping<,>)).GetRuntimeProperty("Key");
                 *  }
                 * }*/

                if (!member.DeclaringType.IsAssignableFrom(currentExpression.Type))
                {
                    currentExpression = Expression.TypeAs(currentExpression, member.DeclaringType);
                }

                blockExpressions.Add(
                    Expression.IfThen(
                        Expression.Equal(nullConstantExpression, currentExpression),
                        Expression.Return(returnLabel, Expression.Default(memberType))));

                blockExpressions.Add(
                    Expression.Assign(
                        memberVariable,
                        Expression.MakeMemberAccess(currentExpression, GetMemberForRead(member))));

                currentExpression = memberVariable;
            }

            blockExpressions.Add(Expression.Label(returnLabel, currentExpression));

            return(Expression
                   .Lambda <Func <object, object> >(
                       Expression.Block(blockVariables, blockExpressions),
                       parameter)
                   .Compile());
        }