Ejemplo n.º 1
0
        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;
                    }
                }
            }
        }
Ejemplo n.º 2
0
        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;
                    }
                }
            }
        }