mercoledì 4 gennaio 2012

How To: Dynamic Class programmatically (2.0)

Questa mattina di buona lena mi sono messo in mente di verificare il funzionamento della precedente versione e mi sono accorto di alcuni problemi / errori che con questa versione risultano corretti.

Uno dei grossi problemi di questo blog è l'impossibilità di inserire gli allegati, di contro quindi pubblico tutta la soluzione in modo da dare qualche spigazioncina in più.

progetto XA.EF_Commons

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XA.EF_Commons.Entities
{
public class _EntitiesType:List<_EntityType>
{
}
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XA.EF_Commons.Entities
{
public class _EntityPropertyType
{
public string propertyName { get; set; }
public string propertyType { get; set; }
public bool readOnly { get; set; }
public string propertyComment { get; set; }
}
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XA.EF_Commons.Entities
{
public class _EntityType
{
public string className { get; set; }
public string classComment { get; set; }
public bool hasCollection { get; set; }
public List<_EntityPropertyType> classProperties =
new List<_EntityPropertyType>();
}
}


Mi sono anche premurato di creare un interfaccia per il builder nel caso mi venisse voglia di creare qualcosa di differente pur lasciando la vecchia modalità.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XA.EF_Commons
{
public interface IBuilder
{
void Build(
string Namespace,
out string[] errors,
out string libraryPath,
out string sourceCode,
XA.EF_Commons.Entities._EntitiesType el
);
}
}


Questa parte del processo è dedicata alla creazione della libreria...


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

namespace XA.EF_Core.Builder
{
public class Builder : XA.EF_Commons.IBuilder
{

/// <summary>
/// Metodo Build
/// </summary>
/// <param name="Namespace">Namespace del progetto</param>
/// <param name="errors">OUT Errori in generazione</param>
/// <param name="libraryPath">OUT Percorso della libreria</param>
/// <param name="sourceCode">OUT Codice Sorgente</param>
/// <param name="el">Collezione di Entità da realizzare</param>
public static void Build(
string Namespace,
out string[] errors,
out string libraryPath,
out string sourceCode,
XA.EF_Commons.Entities._EntitiesType el
)
{
CSharpCodeProvider cProvider = new CSharpCodeProvider();
ICodeCompiler cCompiler = cProvider.CreateCompiler();

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

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

CodeNamespace customEntityRoot = new CodeNamespace(Namespace);
unit.Namespaces.Add(customEntityRoot);

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


foreach (XA.EF_Commons.Entities._EntityType e in el)
{
CodeTypeDeclaration Entity = new CodeTypeDeclaration(e.className);
customEntityRoot.Types.Add(Entity);

for (int i = 0; i < e.classProperties.Count; i++)
{
CodeMemberField cf = new CodeMemberField();
cf.Attributes = MemberAttributes.Private;
cf.Name = "_" + e.classProperties[i].propertyName;
cf.Type = new CodeTypeReference(
e.classProperties[i].propertyType);
Entity.Members.Add(cf);


CodeMemberProperty cp = new CodeMemberProperty();
cp.Name = e.classProperties[i].propertyName;
cp.Type = new CodeTypeReference(
e.classProperties[i].propertyType);
cp.HasGet = true;
cp.GetStatements.Add(new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(null,cf.Name)));
cp.HasSet = true;
cp.SetStatements.Add(new CodeAssignStatement(
new CodeFieldReferenceExpression(null, cf.Name),
new CodeFieldReferenceExpression(null, "value")));

cp.Attributes = MemberAttributes.Public;

Entity.Members.Add(cp);
}

CodeTypeDeclaration EntityList = new CodeTypeDeclaration(
e.className + "_List");
customEntityRoot.Types.Add(EntityList);

CodeTypeReference baseClass = new CodeTypeReference(
"List<"+e.className+">");
EntityList.BaseTypes.Add(baseClass);
}

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()
);



sourceCode = generatedCode.ToString();

libraryPath = results.PathToAssembly;


errors = new string[results.Output.Count];
for (int i = 0; i < results.Output.Count; i++)
{
errors[i] = results.Output[i];
}

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

}
}
}



Non è difficile notare la presenza di due nuovi elementi.
a) CodeMemberField cf = new CodeMemberField();
Che rappresenta il campo interno rimappato dall'entità
b) cp.GetStatements & cp.SetStatements
Che rappresentano i due corpi del set e del get.

Lo scopo di questa breve soluzione, è quello di restituire il nome della dll buildata ed eventualmente gli errori.

Chiaramente se gli errori sono "pieni" dovranno essere cominicati.. caso contrario la libreria sarà disponibile.
Il suggerimento è quello di copiare la libreria nella directory di rilascio e quindi di linkarla al progetto.

Nessun commento: