private static void GenerateReturnsStructure(MethodInfo interfaceMethod, ModuleBuilder moduleBuilder, ILGenerator mIL) { // if we are returning a task, unwrap the task result var returnType = interfaceMethod.ReturnType; if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task <>)) { returnType = returnType.GetGenericArguments()[0]; } // if there are returns attributes specified, then build the structure for the coder var returnsAttributes = interfaceMethod.GetCustomAttributes(true).OfType <RecordsetAttribute>().ToDictionary(r => r.Index); if (returnType.IsSubclassOf(typeof(Results)) && !returnType.Name.StartsWith("Results`", StringComparison.OrdinalIgnoreCase)) { // we have a return that is derived from results mIL.Emit(OpCodes.Ldsfld, typeof(DerivedResultsReader <>).MakeGenericType(returnType).GetField("Default")); } else { bool isSingle = !returnType.IsSubclassOf(typeof(Results)) && !IsGenericListType(returnType); // we are returning results<T...>, or IList<T...> var returnTypeArgs = isSingle ? new Type[] { returnType } : returnType.GetGenericArguments(); Type currentType = null; // go through all of the type arguments or recordsets int returnIndex = 0; for (int i = 0; i < Math.Max(returnTypeArgs.Length, returnsAttributes.Keys.MaxOrDefault(k => k + 1)); i++) { RecordsetAttribute r; returnsAttributes.TryGetValue(i, out r); var types = (r != null) ? r.Types : new Type[] { returnTypeArgs[i] }; // grab the records field for the appropriate OneToOne mapping mIL.Emit(OpCodes.Ldsfld, Query.GetOneToOneType(types).GetField("Records", BindingFlags.Public | BindingFlags.Static)); // keep track of the type that we are returning if (r == null || !r.IsChild) { returnIndex++; } if (i == 0) { // start the chain of calls if (isSingle) { var recordReader = typeof(IRecordReader <>).MakeGenericType(returnType); var readerType = typeof(SingleReader <>).MakeGenericType(returnType); var constructor = readerType.GetConstructor(new Type[] { recordReader }); mIL.Emit(OpCodes.Newobj, constructor); currentType = readerType; } else if (returnType.IsSubclassOf(typeof(Results))) { var method = typeof(Query).GetMethod("ReturnsResults", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(types[0]); currentType = method.ReturnType; mIL.Emit(OpCodes.Call, method); } else { // IList<T> or List<T>, etc... var oneToOneBaseType = typeof(OneToOne <>).MakeGenericType(types[0]); currentType = typeof(ListReader <>).MakeGenericType(types[0]); Type readerType = currentType; // if we're not returning an IList, then we need to insert the reader adapter class if (returnType.GetGenericTypeDefinition() != typeof(IList <>)) { readerType = typeof(ListReaderAdapter <,>).MakeGenericType(returnType, types[0]); } mIL.Emit(OpCodes.Newobj, readerType.GetConstructor(new Type[] { oneToOneBaseType })); } } else if (r != null && r.IsChild) { // the parent is single and we haven't overridden the id field var parentType = currentType.GetGenericArguments()[0]; var childType = types[0]; var list = ChildMapperHelper.GetListSetter(parentType, childType, r.Into); var listMethod = typeof(ClassPropInfo).GetMethod("CreateSetMethod").MakeGenericMethod(parentType, typeof(List <>).MakeGenericType(childType)).Invoke(list, Parameters.EmptyArray); MethodInfo method; if (r.Id == null && TypeIsSingleReader(currentType)) { // previous and recordReader are on the stack, add the list method new StaticFieldStorage(moduleBuilder, listMethod).EmitLoad(mIL); method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single( mi => mi.Name == "ThenChildren" && mi.GetGenericArguments().Length == 2 && currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name) .MakeGenericMethod( new Type[] { parentType, // TParent childType // TChild }); } else { var id = ChildMapperHelper.GetIDAccessor(parentType, r.Id); var idType = id.MemberType; var idMethod = typeof(ClassPropInfo).GetMethod("CreateGetMethod").MakeGenericMethod(parentType, idType).Invoke(id, Parameters.EmptyArray); // previous and recordReader are on the stack, add the id and list methods new StaticFieldStorage(moduleBuilder, idMethod).EmitLoad(mIL); new StaticFieldStorage(moduleBuilder, listMethod).EmitLoad(mIL); method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single( mi => mi.Name == "ThenChildren" && mi.GetGenericArguments().Length == 3 && currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name && mi.GetParameters().Any(p => String.Compare(p.Name, "id", StringComparison.OrdinalIgnoreCase) == 0)) .MakeGenericMethod( new Type[] { parentType, // TParent childType, // TChild idType // TId }); } mIL.Emit(OpCodes.Call, method); currentType = method.ReturnType; } else { var method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(mi => mi.Name == "Then" && mi.GetGenericArguments().Length == returnIndex && mi.GetParameters()[0].ParameterType.Name.StartsWith("IQueryReader", StringComparison.OrdinalIgnoreCase)) .MakeGenericMethod(returnTypeArgs.Take(returnIndex).ToArray()); mIL.Emit(OpCodes.Call, method); currentType = method.ReturnType; } } } }
private static void GenerateReturnsStructure(MethodInfo interfaceMethod, ILGenerator mIL) { // if we are returning a task, unwrap the task result var returnType = interfaceMethod.ReturnType; if (returnType.GetTypeInfo().IsGenericType&& returnType.GetGenericTypeDefinition() == typeof(Task <>)) { returnType = returnType.GetGenericArguments()[0]; } // if there are returns attributes specified, then build the structure for the coder var returnsAttributes = interfaceMethod.GetCustomAttributes(true).OfType <RecordsetAttribute>().ToDictionary(r => r.Index); if (returnType.IsSubclassOf(typeof(Results)) && !returnType.Name.StartsWith("Results`", StringComparison.OrdinalIgnoreCase)) { // we have a return that is derived from results mIL.Emit(OpCodes.Ldsfld, typeof(DerivedResultsReader <>).MakeGenericType(returnType).GetField("Default")); } else { bool isSingle = !returnType.IsSubclassOf(typeof(Results)) && !IsGenericListType(returnType); // we are returning results<T...>, or IList<T...> var returnTypeArgs = isSingle ? new Type[] { returnType } : returnType.GetGenericArguments(); Type currentType = null; // go through all of the type arguments or recordsets int returnIndex = 0; for (int i = 0; i < Math.Max(returnTypeArgs.Length, returnsAttributes.Keys.MaxOrDefault(k => k + 1)); i++) { RecordsetAttribute r; returnsAttributes.TryGetValue(i, out r); var types = (r != null) ? r.Types : new Type[] { returnTypeArgs[i] }; // if the return type is a named tuple, emit column mappings var elementNames = interfaceMethod.ReturnTypeCustomAttributes.GetCustomAttributes(true).OfType <System.Runtime.CompilerServices.TupleElementNamesAttribute>().FirstOrDefault(); if (elementNames != null) { var overrides = elementNames.TransformNames.Select((n, index) => new ColumnOverride(n, String.Format("Item{0}", index + 1))); var oneToOne = System.Activator.CreateInstance(Query.GetOneToOneType(types), new object[] { null, overrides, null }); StaticFieldStorage.EmitLoad(mIL, oneToOne); } else { // grab the records field for the appropriate OneToOne mapping mIL.Emit(OpCodes.Ldsfld, Query.GetOneToOneType(types).GetField("Records", BindingFlags.Public | BindingFlags.Static)); } // keep track of the type that we are returning if (r == null || !r.IsChild) { returnIndex++; } if (i == 0) { // start the chain of calls if (isSingle) { var recordReader = typeof(IRecordReader <>).MakeGenericType(returnType); var readerType = typeof(SingleReader <>).MakeGenericType(returnType); var constructor = readerType.GetConstructor(new Type[] { recordReader }); mIL.Emit(OpCodes.Newobj, constructor); currentType = readerType; } else if (returnType.IsSubclassOf(typeof(Results))) { var method = typeof(Query).GetMethod("ReturnsResults", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(types[0]); currentType = method.ReturnType; mIL.Emit(OpCodes.Call, method); } else { // IList<T> or List<T>, etc... var oneToOneBaseType = typeof(OneToOne <>).MakeGenericType(types[0]); currentType = typeof(ListReader <>).MakeGenericType(types[0]); Type readerType = currentType; // if we're not returning an IList, then we need to insert the reader adapter class if (returnType.GetGenericTypeDefinition() != typeof(IList <>)) { readerType = typeof(ListReaderAdapter <,>).MakeGenericType(returnType, types[0]); } mIL.Emit(OpCodes.Newobj, readerType.GetConstructor(new Type[] { oneToOneBaseType })); } } else if (r != null && r.IsChild) { // the parent is single and we haven't overridden the id field var parentType = currentType.GetGenericArguments()[0]; var childType = types[0]; var list = ChildMapperHelper.GetListSetter(parentType, childType, r.Into); var listMethod = typeof(ClassPropInfo).GetMethod("CreateSetMethod").MakeGenericMethod(parentType, typeof(List <>).MakeGenericType(childType)).Invoke(list, Parameters.EmptyArray); MethodInfo method; if (r.Id == null && TypeIsSingleReader(currentType)) { // previous and recordReader are on the stack, add the id and list method StaticFieldStorage.EmitLoad(mIL, listMethod); method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single( mi => mi.Name == "ThenChildren" && mi.GetGenericArguments().Length == 2 && currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name) .MakeGenericMethod( new Type[] { parentType, // TParent childType, // TChild }); } else { var recordReaderType = typeof(RecordReader <>).MakeGenericType(childType); var parentid = ChildMapperHelper.GetIDAccessor(parentType, r.Id); var parentIdType = parentid.MemberType; var parentIdMethod = parentid.GetType().GetMethod("CreateGetMethod").MakeGenericMethod(parentType, parentIdType).Invoke(parentid, Parameters.EmptyArray); // if groupby is specified, then convert the RecordReader to an IChildRecordReader by groupby if (r.GroupBy != null) { var childid = ChildMapperHelper.FindParentIDAccessor(childType, r.GroupBy, parentType); if (childid == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot find GroupBy {0} on {1}", r.GroupBy, childType.FullName)); } var getMethod = childid.GetType().GetMethod("CreateGetMethod").MakeGenericMethod(childType, parentIdType).Invoke(childid, Parameters.EmptyArray); StaticFieldStorage.EmitLoad(mIL, getMethod); var groupByMethod = recordReaderType.GetMethod("GroupBy").MakeGenericMethod(parentIdType); mIL.Emit(OpCodes.Call, groupByMethod); recordReaderType = groupByMethod.ReturnType; } // previous and recordReader are on the stack, add the id and list methods StaticFieldStorage.EmitLoad(mIL, parentIdMethod); StaticFieldStorage.EmitLoad(mIL, listMethod); method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single( mi => mi.Name == "ThenChildren" && mi.GetGenericArguments().Length == 3 && currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name && mi.GetParameters()[1].ParameterType.Name == recordReaderType.Name && mi.GetParameters().Any(p => String.Compare(p.Name, "id", StringComparison.OrdinalIgnoreCase) == 0)) .MakeGenericMethod( new Type[] { parentType, // TParent childType, // TChild parentIdType // TId }); } mIL.Emit(OpCodes.Call, method); currentType = method.ReturnType; } else { var method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(mi => mi.Name == "Then" && mi.GetGenericArguments().Length == returnIndex && mi.GetParameters()[0].ParameterType.Name.StartsWith("IQueryReader", StringComparison.OrdinalIgnoreCase)) .MakeGenericMethod(returnTypeArgs.Take(returnIndex).ToArray()); mIL.Emit(OpCodes.Call, method); currentType = method.ReturnType; } } } }