Help required in adding new methods, properties into existing classes dynamically
I am not sure whether it is possible to achieve this kind of implementation in Dot Net. Below are the information
Currently we are on an application which is done in COM+, ASP, XSL, XML technologies. It is a multi tier architecture application in which COM+ acts as the BAL. The execution steps for any CRUD operation will be defined using a seperate UI which uses XML to store the information. BAL reads the XML and understands the execution steps which are defined and executes corresponding methods in DLL. Much like EDM we have our custom model (using XML) which determines which property of object is searchable, retrievable开发者_JAVA百科 etc. Based on this information BAL constructs queries and calls procedures to get the data.
In the current application both BAL and DAL are heavily customizable without doing any code change. the results will be transmitted to presentation layer in XML format which constructs the UI based on the data recieved.
Now I am creating a migration project which deals with employee information. It is also going to follow the N Tier architecture in which the presentation layer communicates with BAL which connects to DAL to return the Data.
Here is the problem, In our existing version we are handling every information as XML in its native form (no converstion of object etc), but in the migration project, Team is really interested in utilizing the OOP model of development where every information which is sent from BAL need to be converted to objects of its respective types (example employeeCollection, Address Collection etc).
If we have the static number of data returned from BAL we can have a class which contains those nodes as properties and we can access the same. But in our case the data returned from our BAL need to be customized. How can we handle the customization in presentation layer which is converting the result to an Object.
Below is an example of the XML returned
<employees>
<employee>
<firstName>Employee 1 First Name</firstName>
<lastName>Employee 1 Last Name</lastName>
<addresses>
<address>
<addressType>1</addressType>
<StreetName>Street name1</StreetName>
<RegionName>Region name</RegionName>
<address>
<address>
<addressType>2</addressType>
<StreetName>Street name2</StreetName>
<RegionName>Region name</RegionName>
<address>
<address>
<addressType>3</addressType>
<StreetName>Street name3</StreetName>
<RegionName>Region name</RegionName>
<address>
<addresses>
</employee>
<employee>
<firstName>Employee 2 First Name</firstName>
<lastName>Employee 2 Last Name</lastName>
<addresses>
<address>
<addressType>1</addressType>
<StreetName>Street name1</StreetName>
<RegionName>Region name</RegionName>
<address>
<address>
<addressType>2</addressType>
<StreetName>Street name2</StreetName>
<RegionName>Region name</RegionName>
<address>
<addresses>
</employee>
</employees>
If these are the only columns then i can write a class which is like
public class Address{
public int AddressType {get;set;};
public string StreetName {get;set;};
public string RegionName {get;set;};
}
public class Employee{
public string FirstName {get; set;}
public string LastName {get; set;}
public string AddressCollection {get; set;}
}
public class EmployeeCollection : List<Employee>{
public bool Add (Employee Data){
....
}
}
public class AddressCollection : List<Address>{
public bool Add (Address Data){
....
}
}
This class will be provided to customers and consultants as DLLs. We will not provide the source code for the same.
Now when the consultants or customers does customization(example adding country to address and adding passport information object with employee object) they must be able to access those properties in these classes, but without source code they will not be able to do those modifications.which makes the application useless. Is there is any way to acomplish this in DotNet.
I thought of using Anonymous classes but, the problem with Anonymous classes are
we can not have methods in it. I am not sure how can i fit the collection objects (which will be inturn an anonymous class) Not sure about datagrid / user control binding etc.
I also thought of using CODEDom to create classes runtime but not sure about the meory, performance issues. also the classes must be created only once and must use the same till there is another change.
Kindly help me out in this problem. Any kind of help meterial/ cryptic code/ links will be helpful.
Update:
Thanks for the answers that are provided which gave me to look into different areas which i had not thought of.
There is one peice of info i missed in the question. The UI code also will be as DLLs not as source code. The customers are free to create new Code and attach to the UI but they cannot change the existing UI through code. We are having a designer which will display the selectable, displayable properties from which the customer can select to display data to user. This way they can change the look and feel of the UI. Also they can change the binding property using this model.
If i had given this piece in the question i might have got some more answer and might given you a clear idea on what exactly is happening.
With the above piece, below is my new thought
I am also thinking something of using CodeDom model to create the classes seperately and migrating the same to UI. Below is my thought
Create a console application which reads a XML file of our structure. it will contains the properties and classes that are available in the system. ALso create an UI to manipulate the XML file to create classes. in the console Use COdeDom to generate the classes and methods. Compile them using libraries or shell commands. Move the created classes to UI, Now UI will recogonize the new classes and can display the properties.
This might not create any memory hogging and high memory utilization/leaks. also we are replacing the DLLs which is simple. Only problem is the class generation. it must be sophasticated in a way the team must be able to perform any operation in this UI. what do you guys think
If you allow them to inherit new Address and Employee classes from your base classes that should do the trick.
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string AddressCollection { get; set; }
}
public class EmployeeCollection<T> : List<T> where T : Employee
{
public bool Add(T Data)
{
return this.Add(Data);
}
}
Then a derived employee would look something like this
public class EmployeePlus : Employee
{
public string PassportNo { get; set; }
}
If you want the stronly typed collection to be of EmployeePlus you can simple define your collections like this, if you need to...
public class EmployeeCollection<T> : List<T> where T : Employee
{
public bool Add(T Data)
{
return this.Add(Data);
}
}
Now a strongly typed collection of Employee and derived objects can be used as follows
EmployeeCollection<EmployeePlus> employees =
new EmployeeCollection<EmployeePlus>();
Try to work with MSIL
public class RunTimeObject<T> where T : class
{
void EmitGetter(MethodBuilder methodBuilder, FieldBuilder fieldBuilder)
{
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
ilGenerator.Emit(OpCodes.Ret);
}
void EmitSetter(MethodBuilder methodBuilder, FieldBuilder fieldBuilder)
{
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
ilGenerator.Emit(OpCodes.Ret);
}
public object CreateNewObject(T obj)
{
AssemblyName assemblyName = new AssemblyName { Name = "assembly" };
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("module");
TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", TypeAttributes.Public | TypeAttributes.Class);
foreach (var prop in obj.GetType().GetProperties())
{
FieldBuilder field = typeBuilder.DefineField("_" + prop.Name, typeof(string), FieldAttributes.Private);
PropertyBuilder propertyBuilder =
typeBuilder.DefineProperty(prop.Name,
PropertyAttributes.None,
typeof(string),
new Type[] { typeof(string) });
MethodAttributes methodAttributes =
MethodAttributes.Public |
MethodAttributes.HideBySig;
MethodBuilder methodBuilderGetter =
typeBuilder.DefineMethod("get_value",
methodAttributes,
typeof(string),
Type.EmptyTypes);
EmitGetter(methodBuilderGetter, field);
MethodBuilder methodBuilderSetter =
typeBuilder.DefineMethod("set_value",
methodAttributes,
null,
new Type[] { typeof(string) });
EmitSetter(methodBuilderSetter, field);
propertyBuilder.SetGetMethod(methodBuilderGetter);
propertyBuilder.SetSetMethod(methodBuilderSetter);
}
Type dynamicType = typeBuilder.CreateType();
var dynamicObject = Activator.CreateInstance(dynamicType);
var properties = dynamicType.GetProperties();
int count = 0;
foreach (var item in obj.GetType().GetProperties())
properties[count++].SetValue(dynamicObject, item.GetValue(obj, null), null);
return dynamicObject;
}
}
This seems quite an odd setup, but there's a number of ways you could do what you want:
- Class inheritance (probably making your base classes abstract but partial classes are another option)
- working to interfaces instead of classes and letting your customers worry about the implementation
- extension methods (ref1, ref2) which are commonly used to extend third party code, but are not normally expected to be used.
- decorators
Normally speaking I'd expect to be working with Interfaces in your kind of scenario, but if you're also trying to mix in behaviour I'd hazard a guess that refactoring that may be too painful at this point and class inheritance is where you'll end up.
精彩评论