views:

233

answers:

3

Here's the deal:

I have a report designer where users can create reports based on some predefined datasets. They can select a set of columns to include in the report and then, when the report is ran, an IList is created by mapping the NHibernate collection over to the dto class collection using automapper.

The problem with this is that the DTO collection has a load of redundant columns in as it will be populated with all the data regardless of whether or not it's needed.

My solution to this? Why not create a DTO type at runtime, using the information we have and map the nhibernate collection to the dynamically created DTO collection using only the properties needed:

#region create a dto type:
            AssemblyName assemblyName = new AssemblyName();
            assemblyName.Name = "tmpAssembly";
            var assemblyBuilder = System.Threading.Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder module = assemblyBuilder.DefineDynamicModule("tmpModule");

            // create a new type builder
            TypeBuilder typeBuilder = module.DefineType("ReportDto", TypeAttributes.Public | TypeAttributes.Class);

            foreach (var propertyName in propNames)
            {

                // Generate a private field
                FieldBuilder field = typeBuilder.DefineField("_" + propertyName, typeof(string), FieldAttributes.Private);
                // Generate a public property
                PropertyBuilder property =
                    typeBuilder.DefineProperty(propertyName,
                                     PropertyAttributes.None,
                                     typeof(string),
                                     new Type[] { typeof(string) });

                // The property set and property get methods require a special set of attributes:

                MethodAttributes GetSetAttr =
                    MethodAttributes.Public |
                    MethodAttributes.HideBySig;

                // Define the "get" accessor method for current private field.
                MethodBuilder currGetPropMthdBldr =
                    typeBuilder.DefineMethod("get_value",
                                               GetSetAttr,
                                               typeof(string),
                                               Type.EmptyTypes);

                // Intermediate Language stuff...
                ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
                currGetIL.Emit(OpCodes.Ldarg_0);
                currGetIL.Emit(OpCodes.Ldfld, field);
                currGetIL.Emit(OpCodes.Ret);

                // Define the "set" accessor method for current private field.
                MethodBuilder currSetPropMthdBldr =
                    typeBuilder.DefineMethod("set_value",
                                               GetSetAttr,
                                               null,
                                               new Type[] { typeof(string) });

                // Again some Intermediate Language stuff...
                ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
                currSetIL.Emit(OpCodes.Ldarg_0);
                currSetIL.Emit(OpCodes.Ldarg_1);
                currSetIL.Emit(OpCodes.Stfld, field);
                currSetIL.Emit(OpCodes.Ret);

                // Last, we must map the two methods created above to our PropertyBuilder to 
                // their corresponding behaviors, "get" and "set" respectively. 
                property.SetGetMethod(currGetPropMthdBldr);
                property.SetSetMethod(currSetPropMthdBldr);
            }

            Type generetedType = typeBuilder.CreateType();

            // Now we have our type. Let's create an instance from it:
            object generetedObject = Activator.CreateInstance(generetedType);

            #endregion


            Mapper.CreateMap(typeof(MainInvoiceDataSums), generetedType);
            var dto =
                Mapper.Map<IList<MainInvoiceDataSums>, IList<generetedType>>(r);

The problem?

var dto =
                Mapper.Map<IList<MainInvoiceDataSums>, IList<generetedType>>(r);

We can't new up an IList using the generated type as the generic parameter :s

I seem to always run into problems like this. Am I abusing generics? Is this possible? It would make the app a lot faster (once caching and some checks to dissalow the temp assembly to be regenerated etc are added) and a lot less fussy to maintain.

w://

We can't ne up an IList using the generated type as the generic parameter :s

I seem to always run into problems like this. Am I abusing generics? Is this possible? It would make the app a lot faster (once caching and some checks to dissalow the temp assembly to be regenerated etc are added) and a lot less fussy to maintain.

w://

A: 

I've now tried going a step further by calling the mapping methods using reflection:

Type generetedType = typeBuilder.CreateType();

            Type listType = typeof(IList<>).MakeGenericType(generetedType);

            //Mapper.CreateMap:
            MethodInfo createMapInfo = typeof(Mapper).GetMethods().Where(
                                m => m.Name == "CreateMap" &&
                                    m.GetParameters().Count() == 0 &&
                                    m.GetGenericArguments().Count() == 2 &&
                                    m.IsGenericMethod).First();

            MethodInfo createMap = createMapInfo.MakeGenericMethod(new Type[] { typeof(IList<MainInvoiceDataSums>), listType });

            createMap.Invoke(null, null);

            //Mapper.Map
            MethodInfo mapInfo = typeof(Mapper).GetMethods().Where(
                                m => m.Name == "Map" &&
                                    m.GetParameters().Count() == 1 &&
                                    m.GetGenericArguments().Count() == 2 &&
                                    m.IsGenericMethod).First();

            MethodInfo map = mapInfo.MakeGenericMethod(new Type[] { typeof(IList<MainInvoiceDataSums>), listType });

            var dto = map.Invoke(null, new object[] { r });

this still doesn't work though - anyone got any clues?

w://

cvista
+1  A: 

got it!!

dont call createmap passing the generic lists!

MethodInfo createMap = createMapInfo.MakeGenericMethod(new Type[] { typeof(MainInvoiceDataSums), generetedType });

sorted!!

:)

w://

cvista
You should mark this as the answer.
Sohnee
A: 

Why not use the non-generic version of Map?

Mapper.Map(r, typeof(IList), typeof(IList<>).MakeGenericType(new [] { generatedType });

Jimmy Bogard