【手撸一个ORM】第六步、对象表达式解析和Select表达式解析

说明

一个Orm自然不仅仅包含条件表达式,还会有如下的场景:

OrderBy(s => s.StudentName) 
Select<StudentDto>(s => new StudentDto { s.Id, s.Name, SchoolName = s.School.Name})

而应用场景的不同,导致解析的方式也有所不同,在这里我们又定义了两个解析类:[ObjectMemberVisitor] 和 [SelectExpressionResolver]

[ObjectMemberVisitor] 主要用于从表达式中解析出参数的属性名,会自动忽略导航属性

[SelectExpressionResolver] 主要用于查询的Select方法,也是用于从表达式中解析出属性名,与ObjectMemberVisitor不同的是它不会忽略导航属性。

从下面的代码可以看出,两个类虽然功能类似,但是代码差异很大,主要是因为ObjectMemberVisitor的使用场景比较简单,只需要拿到表达式的Member(成员)就可以了,不必考虑太多。但SelectExpressionResolver不同,其解析结果需要反馈给查询工具更多信息,包括Member与Parameter的映射关系等。


 对象表达式解析

using System.Collections.Generic;
using System.Linq.Expressions;

namespace MyOrm.Expressions
{
    public class ObjectMemberVisitor : ExpressionVisitor
    {
        private readonly List<string> _propertyList;

        public ObjectMemberVisitor()
        {
            _propertyList = new List<string>();
        }

        public List<string> GetPropertyList()
        {
            return _propertyList;
        }

        public void Clear()
        {
            _propertyList.Clear();
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression != null && node.Expression.NodeType == ExpressionType.Parameter)
            {
                _propertyList.Add(node.Member.Name);
            }
            return node;
        }

        protected override Expression VisitNew(NewExpression node)
        {
            foreach (var arg in node.Arguments)
            {
                if (arg.NodeType == ExpressionType.MemberAccess)
                {
                    var member = (MemberExpression) arg;
                    if (member.Expression != null && member.Expression.NodeType == ExpressionType.Parameter)
                    {
                        _propertyList.Add(member.Member.Name);
                    }
                }
            }
            return node;
        }
    }
}

Select表达式解析

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using MyOrm.Reflections;

namespace MyOrm.Expressions
{
    public class SelectExpressionResolver
    {
        private readonly List<string> _propertyList;

        private readonly List<SelectResolveResult> _dict;

        private Type _targetType;

        public SelectExpressionResolver()
        {
            _propertyList = new List<string>();
            _dict = new List<SelectResolveResult>();
        }

        public List<SelectResolveResult> GetPropertyList()
        {
            return _dict;
        }

        public Type GetTargetType()
        {
            return _targetType;
        }

        public void Clear()
        {
            _propertyList.Clear();
        }

        public void Visit(LambdaExpression expression)
        {
            if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                VisitMember((MemberExpression)expression.Body);
            }
            else if (expression.Body.NodeType == ExpressionType.MemberInit)
            {
                VisitMemberInit((MemberInitExpression)expression.Body);
            }
            else if(expression.Body.NodeType == ExpressionType.New)
            {
                VisitNew((NewExpression)expression.Body);
            }
        }

        protected Expression VisitMember(MemberExpression node)
        {
            var rootType = node.GetRootType(out var stack);
            if (rootType == ExpressionType.Parameter)
            {
                if (stack.Count == 1)
                {
                    var propertyName = stack.Pop();
                    var memberName = node.Member.Name;

                    _dict.Add(new SelectResolveResult
                    {
                        PropertyName = propertyName,
                        MemberName = memberName,
                        FieldName = ""
                    });
                }
                else if (stack.Count == 2)
                {
                    var propertyName = stack.Pop();
                    var fieldName = stack.Pop();
                    var memberName = node.Member.Name;
                    _dict.Add(new SelectResolveResult
                    {
                        MemberName = memberName,
                        PropertyName = propertyName,
                        FieldName = fieldName
                    });
                }
            }
            return node;
        }

        protected Expression VisitNew(NewExpression node)
        {
            _targetType = node.Type;
            Console.WriteLine(_targetType);
            if (node.Members != null)
            {
                for (var i = 0; i < node.Members.Count; i++)
                {
                    if (node.Arguments[i].NodeType == ExpressionType.MemberAccess)
                    {
                        var member = (MemberExpression) node.Arguments[i];
                        var rootType = member.GetRootType(out var stack);
                        if (rootType == ExpressionType.Parameter)
                        {
                            if (stack.Count == 1)
                            {
                                var propertyName = stack.Pop();
                                var memberName = node.Members[i].Name;

                                _dict.Add(new SelectResolveResult
                                {
                                    PropertyName = propertyName,
                                    MemberName = memberName,
                                    FieldName = ""
                                });
                            }
                            else if (stack.Count == 2)
                            {
                                var propertyName = stack.Pop();
                                var fieldName = stack.Pop();
                                var memberName = node.Members[i].Name;
                                _dict.Add(new SelectResolveResult
                                {
                                    PropertyName = propertyName,
                                    MemberName = memberName,
                                    FieldName = fieldName
                                });
                            }
                        }
                    }
                }
            }

            return node;
        }

        protected void VisitMemberInit(MemberInitExpression node)
        {
            foreach (var binding in node.Bindings)
            {
                var result = new SelectResolveResult { MemberName = binding.Member.Name };
                if (binding.BindingType == MemberBindingType.Assignment)
                {
                    var expression = ((MemberAssignment) binding).Expression;
                    if (expression.NodeType == ExpressionType.MemberAccess)
                    {
                        var member = (MemberExpression)expression;
                        var rootType = member.GetRootType(out var stack);
                        if (rootType == ExpressionType.Parameter)
                        {
                            if (stack.Count == 1)
                            {
                                var propertyName = stack.Pop();
                                var memberName = binding.Member.Name;

                                _dict.Add(new SelectResolveResult
                                {
                                    PropertyName = propertyName,
                                    MemberName = memberName,
                                    FieldName = ""
                                });
                            }
                            else if (stack.Count == 2)
                            {
                                var propertyName = stack.Pop();
                                var fieldName = stack.Pop();
                                var memberName = binding.Member.Name;
                                _dict.Add(new SelectResolveResult
                                {
                                    PropertyName = propertyName,
                                    MemberName = memberName,
                                    FieldName = fieldName
                                });
                            }
                        }
                    }
                }
            }
        }

        private string ResolveStackToField(Stack<string> parameterStack)
        {
            switch (parameterStack.Count)
            {
                case 2:
                {
                    // 调用了导航属性
                    var propertyName = parameterStack.Pop();
                    var propertyFieldName = parameterStack.Pop();
                        
                    return $"{propertyName}.{propertyFieldName}";
                }
                case 1:
                {
                    var propertyName = parameterStack.Pop();
                    return propertyName;
                }
                default:
                    throw new ArgumentException("尚未支持大于2层属性调用。如 student.Clazz.School.Id>10,请使用类似 student.Clazz.SchoolId > 0 替代");
            }
        }
    }

    public class SelectResolveResult
    {
        public string MemberName { get; set; }

        public string PropertyName { get; set; }

        public string FieldName { get; set; }
    }
}

 

posted @ 2019-04-06 23:32  没追求的码农  阅读(529)  评论(0编辑  收藏  举报