Using Expression as a Compiler, to avoid writing conversion code

2009-09-03 15:22:33 +0000

Marc Gravell wrote about [using Expression as a compiler](http://www.infoq.com/articles/expression-compiler). It was a bit of an eye-opener.

There’s a bit in it where he says:

A similar approach [to using Expression for shallow cloning an object] might be used, for example, to map data between DTO entities and entity objects…

Say no more:

    static class Conversion<TInput, TOutput>
    {
        private static readonly Func<TInput, TOutput> Converter;

        static Conversion()
        {
            Converter = CreateConverter();
        }

        public static Func<TInput, TOutput> CreateConverter()
        {
            var input = Expression.Parameter(typeof(TInput), "input");

            // For each property that exists in the destination object, is there a property with the same name in the source object?
            var destinationProperties = typeof(TOutput)
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(prop => prop.CanWrite);
            var sourceProperties = typeof(TInput)
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(prop => prop.CanRead);

            var memberBindings = sourceProperties.Join(destinationProperties,
                sourceProperty => sourceProperty.Name,
                destinationProperty => destinationProperty.Name,
                (sourceProperty, destinationProperty) =>
                    (MemberBinding)Expression.Bind(destinationProperty,
                        Expression.Property(input, sourceProperty)));

            var body = Expression.MemberInit(Expression.New(typeof(TOutput)), memberBindings);
            var lambda = Expression.Lambda<Func<TInput, TOutput>>(body, input);
            return lambda.Compile();
        }

        public static TOutput From(TInput input)
        {
            return Converter(input);
        }
    }

Use it like this:

    CustomerDto customerDto = CustomerService.GetCustomerById(1);
    Customer customer = Conversion<CustomerDto, Customer>.From(customerDto);

I also threw a fluent interface together as well…

    static class Conversion
    {
        internal class ConversionFrom
        {
            private readonly TInput _input;

            public ConversionFrom(TInput input)
            {
                _input = input;
            }

            public TOutput To()
            {
                return Conversion<TInput, TOutput>.From(_input);
            }
        }

        internal class ConversionTo
        {
            public TOutput From(TInput input)
            {
                return Conversion<TInput, TOutput>.From(input);
            }
        }

        public static ConversionFrom From(TInput input)
        {
            return new ConversionFrom(input);
        }

        public static ConversionTo To()
        {
            return new ConversionTo();
        }
    }</pre>

Use it like this:

Customer customer = Conversion.From(customerDto).To();</pre>

...or like this:

Customer customer = Conversion.To().From(customerDto);</pre>