Sunday, March 31, 2013

Dynamic property or Extended property in C#



I was busy to create architecture of asp.net (C#) project. In the time of development, I faced a situation where it will be good to have a dynamic property generation. I did some search, and found many examples. But after going through all, I found something, which is really nice and solve my problem. My problems are-
1>  I need to get all the data fetched from table in DB in a custom and pre-defined model in c#, but sometime DB queries can return some columns which are not in the defined properties of predefined model.
2>  When I do serialization of the model in c#, it should serialize in normal and proper way, with or without extra columns returned from DB.
Example-
Suppose, I define a model –
Public class EmpModel{
Public string EmpName{get; set;}
Public string EmpId{get; set;}
}
And I also write three SQL queries-
SELECT EmpName,EmpId,EmpAddress from tbl_Employee
SELECT EmpName,EmpId,EmpDept from tbl_Employee
SELECT EmpName,EmpId,EmpDOB from tbl_Employee
Now, as you can see, the queries are returning three columns, but the model only has two properties. I cannot manage EmpAddress, EmpDept and EmpDOB in model. As well as, the 3rd field will be different in type also. To manage this situation, I found-
1>     AssemblyBuilder
2>     ModuleBuilder
3>     TypeBuilder
4>     FieldBuilder
5>     PropertyBuilder
6>     MethodAttributes
7>     MethodBuilder

These are the main building blocks of my solution.

The theory of the solution is, I have to create a class which will have EmpModel as a base class and with the properties (Extra properties) needed. All these, need to be done dynamically.

For all these, I managed to write two method sets(Three methods), which will handle everything for me-
MethodSet 1 (to bind DB columns with Model properties)-[Contains two methods]
public void getModelFromObject<T, T1>(object valu, ref List<T> el,Dictionary<string,Type> props) where T1 : T, new()
        {
            DataTable dt = ((DataSet)valu).Tables[0];
            Type dynamicType = getType(typeof(T).Name+ "Ext", props, typeof(T1));
            foreach (DataRow dr in ((DataSet)valu).Tables[0].Rows)
            {
                T item = (T)Activator.CreateInstance(dynamicType);
                getObject<T>(dr, ref item, dt);
                T tsts = item;

                ((List<T>)el).Add(item);

            }
        }

public void getObject<T>(DataRow dr,ref T obj, DataTable dt)
        {
          
            foreach (DataColumn dc in dt.Columns)
            {
                if (obj.GetType().GetProperty(dc.ColumnName) != null)
                {
                   
                        obj.GetType().GetProperty(dc.ColumnName).SetValue(obj, dr[dc.ColumnName], null);
                 
                }
            }
        }

MethodSet 2(to create extended properties)-[Contains one method]
  public Type getType(string dynamicClassName, Dictionary<string, Type> propertyList, Type baseClassType)
        {
            bool isNewProperties = false;

            AssemblyBuilder assemblyBilder = System.Threading.Thread.GetDomain().DefineDynamicAssembly
            (new AssemblyName(dynamicClassName + "Assembly"), AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBilder = assemblyBilder.DefineDynamicModule(dynamicClassName + "Module");

            TypeBuilder typeBilder = moduleBilder.DefineType(dynamicClassName, TypeAttributes.Public | TypeAttributes.Class, baseClassType);

            string propertyName = null;
            Type propertyType = null;
            var baseClassObj = Activator.CreateInstance(baseClassType);
            foreach (var prop in propertyList)
            {
                propertyName = prop.Key;
                propertyType = prop.Value;

                var propExist = baseClassObj.GetType().GetProperty(propertyName);
                if (propExist != null)
                {
                    continue;
                }

                FieldBuilder filedBilder = typeBilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

                PropertyBuilder propertyBilder = typeBilder.DefineProperty(propertyName, System.Reflection.PropertyAttributes.None, propertyType,
                            new Type[] { propertyType });
               
                MethodAttributes GetSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;

                MethodBuilder currGetPropMthdBilder = typeBilder.DefineMethod("geter", GetSetAttr, propertyType, null);

                ILGenerator currtGetIL = currGetPropMthdBilder.GetILGenerator();
                currtGetIL.Emit(OpCodes.Ldarg_0);
                currtGetIL.Emit(OpCodes.Ldfld, filedBilder);
                currtGetIL.Emit(OpCodes.Ret);


                MethodBuilder currSetPropMthdBilder = typeBilder.DefineMethod("seter", GetSetAttr, null, new Type[] { propertyType });


                ILGenerator currSetIL = currSetPropMthdBilder.GetILGenerator();
                currSetIL.Emit(OpCodes.Ldarg_0);
                currSetIL.Emit(OpCodes.Ldarg_1);
                currSetIL.Emit(OpCodes.Stfld, filedBilder);
                currSetIL.Emit(OpCodes.Ret);


                propertyBilder.SetGetMethod(currGetPropMthdBilder);
                propertyBilder.SetSetMethod(currSetPropMthdBilder);
                isNewProperties = true;
            }

            if (isNewProperties == false)
            {
                return baseClassType;
            }
            return typeBilder.CreateType();
        }
And now, the use of these methods-
     List<IEmp> obj = new List<IEmp>();

            Dictionary<string, Type> props= new Dictionary<string, Type>();
            props.Add("EmpAddress", typeof(string));//if more then one add more.
            modelPlatform.init().getModelFromObject<IEmp,Emp>(ds, ref obj, val);//ds- is the dataset returned from DB
           
            return obj;


Same for two other queries also.
That’s it, Enjoy.

No comments: