private static string BuildSelectClauseForInclude(Type targetType, IncludeDetails includeDetails, Dictionary <string, object> includeVariables, string parameterPrefix = "", int parameterIndex = 0, int depth = 1) { var include = includeDetails.Path; if (string.IsNullOrEmpty(include)) { return(BuildSelectClauseForType(targetType, depth)); } var leftPadding = new string(' ', depth * 2); var dotIndex = include.IndexOf(".", StringComparison.InvariantCultureIgnoreCase); var currentIncludeName = dotIndex >= 0 ? include.Substring(0, dotIndex) : include; Type propertyType; var propertyInfo = targetType.GetProperty(currentIncludeName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); var includeName = currentIncludeName.ToCamelCase(); var includeMethodInfo = includeDetails.MethodIncludes.Count > parameterIndex ? includeDetails.MethodIncludes[parameterIndex].Method : null; var includeByMethod = includeMethodInfo != null && currentIncludeName == includeMethodInfo.Name && propertyInfo.PropertyType == includeMethodInfo.ReturnType; if (includeByMethod) { var methodDetails = includeDetails.MethodIncludes[parameterIndex]; parameterIndex++; propertyType = methodDetails.Method.ReturnType.GetTypeOrListType(); var includeMethodParams = methodDetails.Parameters.Where(pair => pair.Value != null).ToList(); includeName = methodDetails.Method.Name.ToCamelCase(); if (includeMethodParams.Any()) { var includeParameters = string.Join(", ", includeMethodParams.Select(pair => pair.Key + ": $" + pair.Key + parameterPrefix + parameterIndex)); includeName = $"{includeName}({includeParameters})"; foreach (var item in includeMethodParams) { includeVariables.Add(item.Key + parameterPrefix + parameterIndex, item.Value); } } } else { propertyType = propertyInfo.PropertyType.GetTypeOrListType(); } if (propertyType.IsValueTypeOrString()) { return(leftPadding + includeName); } var restOfTheInclude = new IncludeDetails(includeDetails.MethodIncludes) { Path = dotIndex >= 0 ? include.Substring(dotIndex + 1) : "" }; var fieldsFromInclude = BuildSelectClauseForInclude(propertyType, restOfTheInclude, includeVariables, parameterPrefix, parameterIndex, depth + 1); fieldsFromInclude = $"{leftPadding}{includeName} {{{Environment.NewLine}{fieldsFromInclude}{Environment.NewLine}{leftPadding}}}"; return(fieldsFromInclude); }
internal static IncludeDetails ParseIncludePath(Expression expression) { string path = null; var withoutConvert = expression.RemoveConvert(); // Removes boxing var memberExpression = withoutConvert as MemberExpression; var callExpression = withoutConvert as MethodCallExpression; if (memberExpression != null) { var parentPath = ParseIncludePath(memberExpression.Expression); if (parentPath == null) { return(null); } var thisPart = memberExpression.Member.Name; path = parentPath.Path == null ? thisPart : (parentPath.Path + "." + thisPart); } else if (callExpression != null) { if (callExpression.Method.Name == "Select" && callExpression.Arguments.Count == 2) { var parentPath = ParseIncludePath(callExpression.Arguments[0]); if (parentPath == null) { return(null); } if (parentPath.Path != null) { var subExpression = callExpression.Arguments[1] as LambdaExpression; if (subExpression != null) { var thisPath = ParseIncludePath(subExpression.Body); if (thisPath == null) { return(null); } if (thisPath.Path != null) { path = parentPath.Path + "." + thisPath.Path; var result = new IncludeDetails(parentPath.MethodIncludes.Union(thisPath.MethodIncludes)) { Path = path }; return(result); } } } } if (callExpression.Method.DeclaringType?.Name == "QueryExtensions") { var parentPath = ParseIncludePath(callExpression.Arguments[0]); if (parentPath == null) { return(null); } path = parentPath.Path == null ? callExpression.Method.Name : parentPath.Path + "." + callExpression.Method.Name; var arguments = callExpression.Arguments.Zip(callExpression.Method.GetParameters(), (argument, parameter) => new { Argument = argument, parameter.Name }).Skip(1).ToDictionary(arg => arg.Name, arg => (arg.Argument as ConstantExpression).Value); var result = new IncludeDetails { Path = path }; result.MethodIncludes.Add(new IncludeMethodDetails { Method = callExpression.Method, Parameters = arguments }); result.MethodIncludes.AddRange(parentPath.MethodIncludes); return(result); } return(null); } return(new IncludeDetails { Path = path }); }