martedì 3 gennaio 2012

How To: Dynamic Class programmatically

Su questo argomento mi sono sbattuto fino a che non ho trovato quello che realmente stavo cercando ...

L'idea di base è di costruirsi a RunTime delle Classi banalmente Entity con Get e Set, di contro tutto quello che ho trovato su WEB, mi ha sempre lasciato perplesso, tremendamente perplesso. Quintali e quintali di codice poco comprensibili.

Rimango dell'idea ( sfide a parte ) che il codice debba comunque essere leggibile e a tal proposito chi deve leggere deve comprendere ciò che sta leggendo.

Ciò detto .. ecco il quanto.

Questa semplice ma pur efficace console application serve a mostrare come generare una classe Entity partendo da una definizione (che potrebbe essere frutto di un XML).

Framework: 4.0
Reference:Microsoft.CSharp


using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom;
using System.CodeDom.Compiler;

namespace cDynamicType
{

public class entity
{
public string className { get; set; }
public string classNamespace { get; set; }
public List<entityPropertyType> classProperties = new List<entityPropertyType>();
}

public class entityPropertyType
{
public string propertyName { get; set; }
public string propertyType { get; set; }
public bool readOnly { get; set; }
}

class Program
{
static void Main(string[] args)
{

entity e = new entity();
e.className = "entityTest";
e.classNamespace = "Test.Entities";
e.classProperties.Add(new entityPropertyType() {
propertyName = "ID",
propertyType = "Int32",
readOnly = false });

e.classProperties.Add(new entityPropertyType() {
propertyName = "Name",
propertyType = "System.string",
readOnly = false });

e.classProperties.Add(new entityPropertyType() {
propertyName = "Description",
propertyType = "System.string",
readOnly = false });


CSharpCodeProvider cProvider = new CSharpCodeProvider();
ICodeCompiler cCompiler = cProvider.CreateCompiler();

CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.dll");

CodeCompileUnit unit = new CodeCompileUnit();
unit.ReferencedAssemblies.Add("System.dll");

CodeNamespace customEntityRoot = new CodeNamespace(e.classNamespace);
unit.Namespaces.Add(customEntityRoot);

customEntityRoot.Imports.Add(new CodeNamespaceImport("System"));

CodeTypeDeclaration Entity = new CodeTypeDeclaration(e.className);
customEntityRoot.Types.Add(Entity);

CodeConstructor EntityClassConstructor = new CodeConstructor();
EntityClassConstructor.Attributes = MemberAttributes.Public;


for (int i = 0; i < e.classProperties.Count; i++)
{
CodeMemberProperty cp = new CodeMemberProperty();
cp.Name = e.classProperties[i].propertyName;
cp.Type = new CodeTypeReference(e.classProperties[i].propertyType);
cp.HasGet = true;
cp.HasSet = true;

Entity.Members.Add(cp);
}


ICodeGenerator cGenerator = cProvider.CreateGenerator();
StringBuilder generatedCode = new StringBuilder();
StringWriter cWriter = new StringWriter(generatedCode);
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = "C";
cGenerator.GenerateCodeFromCompileUnit(unit, cWriter, options);


CompilerResults results = cProvider.CompileAssemblyFromSource(
parameters,
generatedCode.ToString()
);

Console.WriteLine(generatedCode.ToString());

Console.WriteLine(results.PathToAssembly);

Console.ReadKey();

cProvider = null;
cCompiler = null;
parameters = null;
cGenerator = null;
cWriter = null;
}
}
}



Le prime due classi mi servono per rappresentare l'entità di cui fare il render.
di cui entity si porta dietro poco nulla se non il nome della classe, il namespace e le propretà. La seconda sono le proprietà.

Il processo è semplice:
crea il provider,
crea il compiler,
crea il namespace,
aggiunge le referenze,
aggiunge le using,
crea le proprietà,
compila.

Cosa Manca, certo questo codice come sempre o quasi pecca in molto perchè non è del tutto completo tuttavi può essere un buon spunto su cui ragionare.

Nessun commento: