Beispiel #1
0
        /// <summary>
        /// Contructs immutable object from existing one with changed property specified by lambda expression.
        /// </summary>
        /// <typeparam name="TInstance">Immutable object type.</typeparam>
        /// <typeparam name="TValue">Value to set type.</typeparam>
        /// <param name="source">Original immutable object.</param>
        /// <param name="expression">Navigation property specifying what to change.</param>
        /// <param name="value">Value to set in the resulting object.</param>
        /// <returns>Immutable object with changed property.</returns>
        public TInstance With <TInstance, TValue>(TInstance source, Expression <Func <TInstance, TValue> > expression, TValue value)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            var processContext = new ProcessContext <TInstance>()
            {
                Source = source,
                Target = value as object,
                SourceParameterExpression = expression.Parameters.Single(),
                InstanceExpression        = expression.Body
            };

            var actualValue = (TValue)ResolveInstance(processContext);

            if (object.Equals(actualValue, value))
            {
                return(source);
            }

            while (processContext.InstanceExpression != processContext.SourceParameterExpression)
            {
                if (TryProcessMemberExpression(processContext))
                {
                    continue;
                }

                throw new NotSupportedException($"Unable to process expression. Expression: '{processContext.InstanceExpression}'.");
            }

            var target = (TInstance)processContext.Target;

            processContext.AffectedProperties.Reverse();
            OnEmit?.Invoke(source, target, value, processContext.AffectedProperties.ToArray());

            return(target);
        }
Beispiel #2
0
        private object ResolveInstance <TSource>(ProcessContext <TSource> processContext)
        {
            var key = new InstanceExpressionCacheKey(typeof(TSource), processContext.InstanceExpression);
            var compiledExpression = default(ResolveInstanceDelegate <TSource>);

            if (ResolveInstanceExpressionCache.TryGetValue(key, out var resolveInstanceDelegate))
            {
                compiledExpression = (ResolveInstanceDelegate <TSource>)resolveInstanceDelegate;
            }
            else
            {
                var instanceConvertExpression = Expression.Convert(processContext.InstanceExpression, typeof(object));
                var lambdaExpression          = Expression.Lambda <ResolveInstanceDelegate <TSource> >(instanceConvertExpression, processContext.SourceParameterExpression);
                compiledExpression = lambdaExpression.Compile();
                ResolveInstanceExpressionCache[key] = compiledExpression;
            }

            return(compiledExpression.Invoke(processContext.Source));
        }
Beispiel #3
0
        private bool TryProcessMemberExpression <TSource>(ProcessContext <TSource> processContext)
        {
            if (processContext.InstanceExpression is MemberExpression memberExpression && memberExpression.Member is PropertyInfo property)
            {
                processContext.InstanceExpression = memberExpression.Expression;

                var result            = processContext.Target;
                var type              = memberExpression.Expression.Type;
                var instance          = ResolveInstance(processContext);
                var activationContext = GetActivationContext(type, type);
                var arguments         = ResolveActivatorArguments(activationContext.ParameterResolvers, property, instance, ref result);

                processContext.Target = activationContext.Activator.Invoke(arguments);
                processContext.AffectedProperties.Add(property.Name);

                return(true);
            }

            return(false);
        }