Visualizzazione post con etichetta XA.EF Entity framework. Mostra tutti i post
Visualizzazione post con etichetta XA.EF Entity framework. Mostra tutti i post

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.

sabato 27 agosto 2011

XA.EF Entity framework (4)

Benchè nel terzo capitolo abbia speso qualche parola per la costruzione di una proprietà c'e' da aggiungere ancora molto altro. Indubbiamente mancano gli attributi, custom o dipendenti da Linq to Sql.

Verosimilmente il concetto di attributo dovrebbe essere quanto di più esplicativo per il destino della proprietà stessa, ma deve dare anche un "lume" su come potremo utilizzare la proprietà o se questa sarà visibile o meno.

Questo punto diciamo che decisamente "analitico" ossia, dato che l'implementazione è ancora molto lontana posso scegliere arbitrariamente di:

-presentare un form con tutti i campi di una tabella e attribuirgli la visibilità.
-dare la possibilità all'utente di modificare gli attributi agendo sul codice.

A ben pensarci il primo caso sembra sicuramente il più ovvio, di contro portei sfruttare quanto esposto nel post della persistenza, per avere un "registro di quanto implementato" per poi ripresentarlo all'utente che sta rigenerando le classi.

Sempre etendendo il ragionamento se genero tutto io, so che cosa ho generato. Ma gli utenti sono utenti ( e del resto anch'io ) quindi è noto il fatto che lanciato un script si ha "il prurito" di vedere che cosa è sucesso.
Quindi tanto vale lasciare la possibilità all'utente di modificare il codice generato.

Una versione semplificata di questi attributi potrebbe essere resa tramite quanto segue.

using System;

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

namespace XA.EF.Test
{

[System.AttributeUsage(
AttributeTargets.Class|
AttributeTargets.Field|
AttributeTargets.Property,
AllowMultiple = true)]
public class DbMapperAttribute:System.Attribute
{
private string _dbFieldName;
private string _mappingEntity;
private string _headerOrCaption;
private eFieldVisibility _visibility;
private int _columnOrder;

public string dbFieldName
{
get { return _dbFieldName; }
}

public string mappingEntity
{
get { return _mappingEntity; }
}

public string headrOrCaption
{
get { return _headerOrCaption; }
}

public int columnOrder
{
get { return _columnOrder; }
}

public eFieldVisibility visibility
{
get { return _visibility; }
}

public DbMapperAttribute(
int ColumnOrder,
string DbFieldName,
string MappingEntity,
string HeaderOrCaption,
eFieldVisibility Visibility
)
{
_dbFieldName = DbFieldName ;
_mappingEntity = MappingEntity;
_headerOrCaption = HeaderOrCaption;
_visibility = Visibility;
_columnOrder = ColumnOrder;
}
}
}


e per dar maggior chiarezza, questo è l'enumerativo che utilizzo.

using System;

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

namespace XA.EF.Test
{
public enum eFieldVisibility
{
none,
grid,
details,
both
}
}


Tuttavia è facile comprendere che l'attributo derivante servirà anche per mostrare o non mostrare l'elemento in una griglia o in un form di dettaglio.

Ciò detto mi è utile considerare sempre più che per la realizzazione di un E.F. strutturato e elastico è sicuramente necessario scrivere una serie di classi che vadano ben oltre alle entità.

lunedì 15 agosto 2011

XA.EF Entity framework (3)

Per le entity e la collezione rimango ancora due o tre cosuccie da definire.

Dato che a priori non potrò sapere in quale lingua è stato realizzato il db e dato che non voglio studiarmi, quanto meno per il momento, una regular expression, per definire i singolari e i plurali.

Utilizzerò questa convenzione:
-NomeEntita = NomeTabella,
-NomeCollezione = NomeTabella_List,
-NomeAdapter = NomeTabella_Adapter.

Che per il momento ritengo valida e plausibile. Di contro c'e' ancora da considerare come si potrà utilizzare questo prodotto.

La prima idea è stata di realizzare un plugin per la ide, ma non mi ha convinto del tutto. Ritengo che la realizzazione di un prodotto Stand Alone possa dare maggior equilibrio, certo è vero che questo presupposto allontana un po' dall'usabilità stessa.

Quindi tornando alle mie preziose definizioni, direi che per l'entità posso raccogliere:
-Using ( servono sempre )
-Namespace ( per forza )
-Modificatore
-Nome classe
-Elenco di Field
-Elenco di Property
-Flag per creare il costruttore
-Flag per creare il distruttore
-Override di ToString ( che lascerò come fisso ... vedi post: ToString


Questo implica che dovrò implementare un writer overridabile per ogni elemento presente in questa lista.
Non solo devo creare anche un Tipo per i Field e un tipo per le proprietà, in questo modo prevengo il dover effettuare un continuo accesso al db.
Di concetto mi piacerebbe estrarre tutto fin dall'inizio, leggerlo e processarlo. Mi sta quasi balenando l'idea di creare un seriliazzatore anche se forse non è il caso.

public class PropertyPrototype

{
private string _accessType;
private string _valueType;
private string _name;
private string _description;
private bool _isReadOnly;

public string AccessType
{
get { return _accessType; }
set { _accessType = value; }
}

public string ValueType
{
get { return _valueType; }
set { _valueType = value; }
}

public string Name
{
get { return _name; }
set { _name = value; }
}

public string Description
{
get { return _description; }
set { _description = value; }
}

public bool IsReadOnly
{
get { return _isReadOnly; }
set { _isReadOnly = value; }
}
}


Partendo quindi dal prototipo più interessante mi sono ricavato questa classe, che per il momento descrive egreggiamente una proprietà.

public class PropertiesPrototype : List<PropertyPrototype>

{

}


E giusto per non smentirmi questa è la mia collezione.

domenica 14 agosto 2011

XA.EF Entity framework (2)

Il post precedente si era concluso con queste affermazioni:

L'Entità per il mio framework deve:
A)contenere delle proprietà che rappresentino la fonte da cui arriva,
B)contenere un metodo o un delegato che ne esegua la traccia,
C)contenere un evento che mi sappia indicare che lo stato è cambiato.


La collezione di elementi per il mio Entity Framork:
A)sarà reso con un lista,
B)conterrà i commenti come per l'entità,
C)non avrà bisogno d'altro.


Entrambe vere e parzialmente incomplete. Nel senso che mi ero già permesso di asserire, che avrei lasciato spazio a più possibilità. Quindi questo implica che volendo un utente potrà comunque scrivere una sua entità, magari indipendente dalla base dati da cui siamo partiti per realizzare le nostre entità.

Quindi potrbbe tornarci utile avere una base astratta che rappresenti l'entità

abstract class XAEntiy

{
public event EventHandler

}

delegate void XAEntityChangedEventHandler(XAEntityChangedEventArg Args);


class XAEntityChangedEventArg : EventArgs
{
public string ActualEntityValue { get; set; }

public XAEntityChangedEventArg( string Arg )
{
ActualEntityValue =Arg ;
}
}


abstract class XAEntiy
{
public event XAEntityChangedEventHandler StatusChanged;
}


Il tutto potrebbe sembra decisamente incomprensibile, ma analizzando il codice scopriamo che :
delegate void XAEntityChangedEventHandler(XAEntityChangedEventArg Args);


Sarà il nostro delegato che firma l'evento.

In più abbiamo anche gli eventuli argomenti legati all'evento, e di seguito la classe
astratta.

abstract class XAEntiy

{
public event XAEntityChangedEventHandler StatusChanged;
}



Tuttavia, vorrei anche implementare un interfaccia IXAEntity che ci darà la possibilità di avere un legame forte con la struttra.

In tutto questo emerge che il mio E.F. fondamentalmente si occuperà di scrivere le classi e di renderle sempre disponibili. Verosimilmente l'idea ora mi convince, tuttavia ho qualche dubbio sulla sincornizzazione.

Quindi :

L'Entità per il mio framework deve:
A)contenere delle proprietà che rappresentino la fonte da cui arriva,
B)contenere un metodo o un delegato che ne esegua la traccia,
C)contenere un evento che mi sappia indicare che lo stato è cambiato,
E)Implementeranno un interfaccia, un contratto solido,
F)una base astratta per tutti quello che non dovrà essere reimplemenato.
G)Le mie classi saranno scritte fisicamente nella soluzione.

XA.EF Entity framework (1)

Dopo una ricerca di quello che il web propone, mi sono proposto di realizzare il mio Entity Framework.

Mi sono deciso di pubblicare tutto il mio processo di realizzazione, perchè sono sicuro che tanti come me si sono un po' stancati di scaricare "cose" non estensibili, di provare del codice che "se c'e' uno spazio in una cartella non va", di agganciarsi librerie che "non vanno se non siete connessi a internet".

Che cos'e' un Entity Framework
Semplicemente si tratta di un processo, che data una connessione ad una fonte dati abbia la capacità di ricostruire sotto forma di codice le classi che rappresentano lo schema dati.
Tante parole che spiegano e non spiegano, ma come al solito un esempio vale più di mille parole.

Supponiamo di aver un Db che contiene due Tabelle:
Amici
- idAmico
- idTipoAmico
- Cognome
- Nome

TipoAmici
-idTipo_Amico
-Nome_TipoAmico
-Descrizione_TipoAmico

Un entity framework che si rispetti, dovrebbe, dopo un analisi del db, fornirci come Entity:

class Amico

{
public int idAmico {get;set;}
public int idTipoAmico { get; set;}
public string Cognome {get; set;}
public string Nome {get; set;}
}

class Amici :List<Amico>
{
}

class TipoAmico
{
public int idTipoAmico { get; set;}
public string Nome_TipoAmico { get; set;}
public string Descrizione_TipoAmico { get; set;}
}

class TipoAmici :List<TipoAmico>
{
}


Ma, ci dovrebbe fornire anche qualcosa in più, ossia la classe che si occuperà fisicamente di leggere e scrivere su DB, ossia la classe che si occuperà del vero lavoro.

class AmicoAdapter

{
public int Insert(Amico item)
{
// istruzione sql per insert
}
public void Edit(Amico item)
{
// istruzione sql per update
}
public void Delete(Amico Item)
{
// istruzione sql per delete
}
public List<Amico> Select ()
{
// istruzione sql per select
{
}



Naturalmente si tratta di esempi e quindi non mi cimenterò nella scrittura approfondita del codice. Anzi, per il momento non la considero proprio.

A bene vedere e a ben comprendere un Entity Framework altro non fa che "levarci le castagne dal fuoco" ossia realizza per noi delle classi che ci serviranno per la realizzazione di un progetto.


Perchè voglio realizzare un Entity Framework
La domanda è lecita, ma la risposta non è così scontanta.

Ogni azienda, grande o piccola deve provvedere alla tutela della sua proprietà intellettuale, al valore delle proprie buone pratiche e alla qualità di ciò che fornisce e fornirà ai suoi clienti.

E' quindi un MUST to Have provvedere alla realizzazione di un qualcosa che in parte assolva o garantisca un certo livello di qualità in questo compito.

Ponetevi la questione da un livello più alto, se vi trovate a dovere realizzare un applicazione con un db da 100 tabelle, davvero il primo pensiero e di raelizzare una ad una 100 classi per le entità e altrettante 100 per le collezioni? Direte al vostro manager, capo progetto, amministratore delegato che vi ci vorranno 100 giorni solo per
realizzare il telaio della base dati ?? Sono scelte io non lo farei...

Quindi realizzo questo Entity Framework sulla base della mia esigenza, ripromettendomi però di lasciare tanto spazio libero, non solo alle interpretazioni, ma a eventuali "plugin" che potranno dare maggior beneficio a chi "sfrutterà questa base".

Chiaramente, pubblicherò la maggior parte del codice, ma mi riservo di tenere qualcosa per me...

Che cosa mi/vi servirà
-SqlExpress
-VisualStudio
-C#
-Linq
-Conoscenze di base sugli oggetti Ado.Net
-Reflection ( credo proprio che mi servirà )
-Generic ( come sopra )
-Un po di conoscenza dei principali pattern.

Che cos'e' un Entity e che cosa deve Fare
Per quel che mi riguarda un entità altro non è che una classe, il cui scopo è di contenere delle informazioni relative ad una astrazione di dati.

Con astrazione di dati intendo che al momento non so e non voglio sapere da dove provengono e perchè.

Quindi l'esempio Amico, potrebbe andare anche bene, tuttavia si tratta di un qualcosa
di troppo passivo. Contiene e non fa altro, e ciò non è corretto.

Un esempio dovrebbe chiarire meglio la situazione:
Durante un processo d'inserimento dei dati in Amico, se volessi tracciarne il contenuto, dovrei interrogare proprietà per proprietà, con Amico la cosa è presto fatta, ma difficilmente ci va così bene.

Quindi alla mia classe Amico, manca un metodo che tracci il suo cambiamento e verosimilmente un evento che ci comunichi che lo stato è cambiato.

L'Entità per il mio framework deve:
A)contenere delle proprietà che rappresentino la fonte da cui arriva,
B)contenere un metodo o un delegato che ne esegua la traccia,
C)contenere un evento che mi sappia indicare che lo stato è cambiato.


E già che ci siamo visto che sono parecchio pignolo, la mia classe, dovrà
già anche implementare i commenti come summaries, perchè a ma piace avere un prodotto completo.

In questi commenti, mi piacerebbe anche avere l'autore, la data di creazione e nominalmente la fonte di provenienza.

Tuttavia, mi sembra che la mia Entità non sia ancora del tutto completa, perchè se per esempio dovessi avere due amici con lo stesso nome e con lo stesso cognome ?

Sarebbe carino avere la possibilità di clonare l'entità già implementando il necessario per farne una copia ? no ? NO! assolutamente no. Un entità deve essere anche un filo Stupida, perchè alla fine dei conti è solo un contenitore, che al massimo ci può dire quando è pieno.


Perchè usare una lista e non una dataTable
Mi appello al 5 emendamento di uno stato che non è il mio... ma avete presente quanto pesa una dataTable ? avete provato a usare Linq su una dataTable ? è un po come se per uccidere un insetto si ricorresse ad una bomba atomica.

Quindi userò una list senza troppi complimenti.

La collezione di elementi per il mio Entity Framork:
A)sarà reso con un lista,
B)conterrà i commenti come per l'entità,
C)non avrà bisogno d'altro.