【手撸一个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; } } }