zoukankan      html  css  js  c++  java
  • 【手撸一个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; }
        }
    }
  • 相关阅读:
    vue typescript 父子组件间值的传递
    flex 布局列表自动换行
    css文字两端对齐
    webstorm windows 常用快捷键
    vue elmentUi el-scrollbar 美化滚动条样式
    简述在Vue脚手架中,组件以及父子组件(非父子组件)之间的传值
    简述Vue的实例属性、实例方法
    Js基本类型中常用的方法总结
    简述Vue中的过滤器
    简述Vue中的计算属性
  • 原文地址:https://www.cnblogs.com/diwu0510/p/10663442.html
Copyright © 2011-2022 走看看