Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="Chinchilla Software Limited">
4 : // // Copyright Chinchilla Software Limited. All rights reserved.
5 : // // </copyright>
6 : // // -----------------------------------------------------------------------
7 : #endregion
8 :
9 : using System;
10 : using System.Collections.Generic;
11 : using System.Linq;
12 : using System.Linq.Expressions;
13 : using System.Reflection;
14 :
15 : namespace Cqrs.Sql.DataStores
16 : {
17 : /// <summary>
18 : /// Converts <see cref="Expression"/> trees.
19 : /// </summary>
20 : public abstract class ExpressionTreeConverter<TA, TB> : ExpressionVisitor, IExpressionTreeConverter
21 1 : {
22 : private readonly Dictionary<ParameterExpression, ParameterExpression> _parameters = new Dictionary<ParameterExpression, ParameterExpression>();
23 :
24 : private readonly Dictionary<MemberInfo, LambdaExpression> _mappings;
25 :
26 : /// <summary>
27 : /// Instantiates a new instance of the <see cref="ExpressionTreeConverter{TA,TB}"/> class
28 : /// </summary>
29 1 : protected ExpressionTreeConverter(Dictionary<MemberInfo, LambdaExpression> mappings)
30 : {
31 : _mappings = mappings;
32 : }
33 :
34 : /// <summary>
35 : /// Instantiates a new instance of the <see cref="ExpressionTreeConverter{TA,TB}"/> class
36 : /// </summary>
37 1 : protected ExpressionTreeConverter()
38 : {
39 : _mappings = GetMappings();
40 : }
41 :
42 : /// <summary>
43 : /// A collection of <see cref="LambdaExpression"/> grouped by <see cref="MemberInfo"/>.
44 : /// </summary>
45 1 : public abstract Dictionary<MemberInfo, LambdaExpression> GetMappings();
46 :
47 : /// <summary>
48 : /// If the <paramref name="node"/> if of <see cref="Type"/> <typeparamref name="TA"/>
49 : /// A <see cref="ParameterExpression"/> of <see cref="Type"/> <typeparamref name="TB"/> will to created with the same name.
50 : /// </summary>
51 1 : protected override Expression VisitParameter(ParameterExpression node)
52 : {
53 : if (node.Type == typeof(TA))
54 : {
55 : ParameterExpression parameter;
56 : if (!_parameters.TryGetValue(node, out parameter))
57 : {
58 : _parameters.Add(node, parameter = Expression.Parameter(typeof(TB), node.Name));
59 : }
60 : return parameter;
61 : }
62 : return node;
63 : }
64 :
65 : /// <summary>
66 : /// If the <see cref="MemberExpression.Member"/> of the provided <paramref name="node"/> matches a mapping in
67 : /// <see cref="GetMappings"/>, then that mapping is executed.
68 : /// </summary>
69 1 : protected override Expression VisitMember(MemberExpression node)
70 : {
71 : if (node.Expression == null || node.Expression.Type != typeof(TA))
72 : return base.VisitMember(node);
73 :
74 : Expression expression = Visit(node.Expression);
75 : if (expression.Type != typeof(TB))
76 : throw new Exception("Whoops");
77 :
78 : LambdaExpression lambdaExpression;
79 : if (_mappings.TryGetValue(node.Member, out lambdaExpression))
80 : return new SimpleExpressionReplacer(lambdaExpression.Parameters.Single(), expression).Visit(lambdaExpression.Body);
81 :
82 : return Expression.Property(expression, node.Member.Name);
83 : }
84 :
85 : /// <summary>
86 : /// Visits the children of the <paramref name="node"/>.
87 : /// </summary>
88 1 : protected override Expression VisitLambda<T>(Expression<T> node)
89 : {
90 : return Expression.Lambda(
91 : Visit(node.Body),
92 : node.Parameters.Select(Visit).Cast<ParameterExpression>()
93 : );
94 : }
95 : }
96 : }
|