private Expression BuildWriterItem(IMappingAssociation association, Expression from, Expression to, ParameterExpression explicitProperties)
        {
            if ((association.Direction & MappingDirection.Write) != MappingDirection.Write)
            {
                return(null);
            }

            var converter = association as IConvertionAssociation;
            var component = association as IComponentAssociation;

            var donor    = association.Target.Apply(@from);
            var acceptor = association.Source.Apply(to);

            Expression result;

            if (component != null)
            {
                //todo: probably should filterout expand somehow. E.g. 'foo.id' should become 'id'. Now simply not passing outer expand
                result = BuildWriterBody(donor, acceptor, null);

                if (result.NodeType == ExpressionType.Default && result.Type == typeof(void))
                {
                    return(null);
                }
            }
            else
            {
                PropertyInfo prop;
                if (!acceptor.TryGetProperty(out prop))
                {
                    throw new InvalidOperationException(
                              string.Format("Mapping from source:{0} to target:{1} marked as writable but source is not a property access expression", association.Source.Stringify(), association.Target.Stringify())
                              );
                }

                result = Expression.Assign(
                    acceptor,
                    converter == null ? donor : converter.TargetConverter.Expression.Apply(donor)
                    );
            }

            if (!(donor.Type.IsValueType || (result.NodeType == ExpressionType.Assign && ((BinaryExpression)result).Right == donor)))
            {
                result = Expression.IfThenElse(
                    donor.CreateCheckForDefault(),
                    Expression.Assign(acceptor, acceptor.Type.GetDefaultExpression()),
                    result
                    );
            }

            if (explicitProperties != null)
            {
                result = Expression.IfThen(
                    Expression.OrElse(
                        explicitProperties.CreateCheckForDefault(),
                        explicitProperties.CreateContains(Expression.Constant(association.Key, typeof(string)))
                        ),
                    result
                    );
            }

            return(result);
        }
        private Expression BuildReaderItem(IMappingAssociation association, Expression from, ParameterExpression expands)
        {
            if ((association.Direction & MappingDirection.Read) != MappingDirection.Read)
            {
                return(null);
            }

            var converter  = association as IConvertionAssociation;
            var collection = association as ICollectionAssociation;
            var component  = association as IComponentAssociation;
            var expandable = association as IExpandableAssociation;

            Expression result;

            var donor = association.Source.Apply(from);

            var targetType = association.Target.ReturnType;

            if (collection != null)
            {
                LambdaExpression selector;

                //selector = new component { ... }
                if (component != null)
                {
                    var param = Expression.Parameter(collection.SourceItemType);

                    //todo: probably should filterout expand somehow. E.g. 'foo.id' should become 'id'. Now simply not passing outer expand
                    var body = BuildReaderBody(param, collection.TargetItemType, null);

                    selector = Expression.Lambda(body, param);
                }
                //selector = converter
                else if (converter != null)
                {
                    selector = converter.TargetConverter.Expression;
                }
                else
                {
                    throw new InvalidOperationException();
                }

                // from.prop.select(selector).toarray().asenumerable()
                result =
                    Expression.Convert(
                        Expression.Call(
                            ToArray.MakeGenericMethod(collection.TargetItemType),
                            Expression.Call(
                                Select.MakeGenericMethod(collection.SourceItemType, collection.TargetItemType),
                                donor,
                                selector
                                )
                            ),
                        targetType
                        );
            }
            else
            {
                // new component { .... }
                if (component != null)
                {
                    //todo: probably should filterout expand somehow. E.g. 'foo.id' should become 'id'. Now simply not passing outer expand
                    result = BuildReaderBody(donor, targetType, null);
                }
                //-or- converter(from.prop)
                else if (converter != null)
                {
                    result = converter.SourceConverter.Expression.Apply(donor);
                }
                // from.prop
                else
                {
                    result = donor;
                }
            }

            // expands.contains({key}) ? {exp} : null
            if (result != null && expands != null && expandable != null && expandable.Expand)
            {
                result = Expression.Condition(
                    expands.CreateContains(Expression.Constant(association.Key, typeof(string))),
                    result,
                    targetType.GetDefaultExpression()
                    );
            }

            // from.prop != null ? null : {exp}
            if (!(donor.Type.IsValueType || result == donor))
            {
                result = Expression.Condition(
                    donor.CreateCheckForDefault(),
                    targetType.GetDefaultExpression(),
                    result
                    );
            }

            return(result);
        }