Pentru PMKB, http://pmkb.codeplex.com/ am dorit sa muncesc cit mai usor. Si, pentru ca adopt o strategie gen Table per Type am considerat ca designul bazei de date este cel al claselor.
Daca va uitati la proiectul PMKB_DAL o sa vedeti urmatoarele fisiere .tt
- Model1.Context.tt – genereaza EFContext si genereaza codul pentru Fluent API, ca de exemplu:
modelBuilder.Entity<Link_Tag>().ToTable(HelperTables.NameTable(Tables_PMKBEntities.Link_Tag));
modelBuilder.Entity<Link_Tag>().HasKey(item => item.LinkTagID);
modelBuilder.Entity<Link_Tag>()
.HasRequired(item => item.Link_LinkID_Link_Tag_LinkID)
.WithMany(u => u.Link_LinkID_Link_Tag_LinkID)
.HasForeignKey(x => x.LinkID)
.WillCascadeOnDelete(false);
- Model1.tt – genereaza codul pentru fiecare clasa + o interfata pentru ea:
public interface I_Link
{
long LinkID{get;set;}
string LinkName{get;set;}
string LinkDescription{get;set;}
string LinkUrl{get;set;}
Nullable<long> CreationUserID{get;set;}
Nullable<System.DateTime> LinkCreationDate{get;set;}
string LinkShortURL{get;set;}
}
public partial class Link: FastDatabaseQuery.IReceiveVisitor, I_Link
{
- ITList.tt – genereaza codul pentru clasa Lista – pentru fiecare tabela pot sa vreau sa incarc mai multe rinduri sau sa sterg ceva rapid:
[System.Diagnostics.DebuggerDisplayAttribute("Link_List Count={Count}")]
public partial class Link_List : ColList<Link>
{
//code
public void Delete_LinkID(long value)
{
base.Conection.Database.ExecuteSqlCommand("delete from " + HelperTables.NameTable(Tables_PMKBEntities.Link) + " where LinkID = {0}",value);
}
Astfel incit codul devine:
using (Link_List ll = new Link_List(ConnectionName))
{
if (string.IsNullOrEmpty(OrderBy))
OrderBy = Link_List.FieldNames.LinkID;
var iq = ll.LoadFromDB.LoadAllQ();
var ordered = Link_FindDB.OrderByField(iq, OrderBy);
if (!string.IsNullOrEmpty(Search))
{
ll.LoadFromDB.AddToCustomPredicate(Link_FindDB.fexp_LinkUrlContainsMultipleDef(Search), false);
ll.LoadFromDB.AddToCustomPredicate(Link_FindDB.fexp_LinkDescriptionContainsMultipleDef(Search), false);
ll.LoadFromDB.AddToCustomPredicate(Link_FindDB.fexp_LinkNameContainsMultipleDef(Search), false);
ordered = ll.LoadFromDB.LoadFindCustomPredicateOrderedQ(OrderBy);
}
return ordered.ToPagedList(pageNumber, PageSize);
}
- Find.tt – genereaza codul pentru cautare in BD ( de exemplu, pentru date pot sa am between) . IN acest exemplu arat fexp_LinkUrlContainsMultipleDef care face Like din Sql :
public static Func<string, Expression<Func<Link, bool>>> fexp_LinkUrlContainsMultipleDef = value => fexp_LinkUrlContainsMultiple(value, "%");
public static Func<string,string, Expression<Func<Link, bool>>> fexp_LinkUrlContainsMultiple = (value,separator) =>
{
value=value.ToLower();
var arr=value.Split(new string[1]{separator}, StringSplitOptions.RemoveEmptyEntries);
switch(arr.Length)//TODO: use a better expression here rahter than based on length
{
case 1:
return (item => item.LinkUrl != null && item.LinkUrl.ToLower().Contains(value));
case 2:
{
string v1=arr[0].Replace(separator,"");
string v2=arr[1].Replace(separator,"");
return (item => item.LinkUrl != null && item.LinkUrl.ToLower().Contains(v1) && item.LinkUrl.ToLower().Contains(v2) );
}
default:
{
string v1=arr[0].Replace(separator,"");
string v2=arr[1].Replace(separator,"");
string v3=arr[2].Replace(separator,"");
return (item => item.LinkUrl != null && item.LinkUrl.ToLower().Contains(v1) && item.LinkUrl.ToLower().Contains(v2) && item.LinkUrl.ToLower().Contains(v3) );
}
}
};
- Resource.tt – genereaza codul pentru fisiere resx – de exemplu , pentru fiecare clasa am nevoie de “edit”, “delete”, “new” , nume de label pentru (aproape) fiecare proprietate
<data name="Accessed_AddNew" xml:space="preserve"><value>Add new Accessed</value></data>
<data name="Accessed_List" xml:space="preserve"><value>List of Accessed</value></data>
<data name="Accessed_Found_Multi" xml:space="preserve"><value>found {0} Accesseds</value></data>
<data name="Accessed_Found_One" xml:space="preserve"><value>One Accessed found</value></data>
<data name="Accessed_Found_Zero" xml:space="preserve"><value>No Accessed found</value></data>
<data name="Accessed_Save" xml:space="preserve"><value>Save Accessed</value></data>
<data name="Accessed_Save_Error" xml:space="preserve"><value>Error saving Accessed</value></data>
<data name="Accessed_Delete_Error" xml:space="preserve"><value>Error deleting Accessed</value></data>
- Visitor.tt – daca am nevoie sa generez ceva date despre fisiere. Implementeaza Visitor pattern din Design Patterns.
public partial class Link: FastDatabaseQuery.IReceiveVisitor, I_Link
public partial class Link_Visitor: FastDatabaseQuery.IPropertiesVisitor
{
public string Visited(FastDatabaseQuery.IReceiveVisitor i)
{
Bineinteles ca fiecare cod are nevoie de imbunatatiri. De aceea imi trebuie cod intercalat – si, de obicei, profit de partial in (aproape ) toate formele sale:
1. la definitia clasei
De exemplu,clasa Link_OLAP trebuia sa implementeze si interfata I_Link . Asa incit am adaugat un fisier .cs in care am scris
partial class Link_OLAP:I_Link
{
}
2. la introducerea unor metode pe care pot sau nu sa le definesc in corpul clasei asociate.
De exemplu, pentru definirea Modelului trebuia sa spun ca LinkID nu este autogenerat( o problema stupida a EF, care incearca sa fie destept)
Cind am facut override la OnModelCreating am definit si urmatoarele:
partial void OnBeginModelCreating(DbModelBuilder modelBuilder,ref bool Continue);
partial void OnFinishModelCreating (DbModelBuilder modelBuilder);
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
bool Continue=true;
OnBeginModelCreating(modelBuilder, ref Continue);
if(!Continue)
return;
//construct default
modelBuilder.Entity<Accessed>().ToTable(HelperTables.NameTable(Tables_PMKBEntities.Accessed));
modelBuilder.Entity<Accessed>().HasKey(item => item.AccessedID);
//other code
OnFinishModelCreating(modelBuilder);
}
Asa ca am adaugat un nou fisier, am pus partial class si am definit OnFinishModelCreating ( Nu era obligatoriu – de ex., nu am definit OnBeginModelCreating)
public partial class PMKBEntities
{
partial void OnFinishModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Link_OLAP>().Property(item => item.LinkID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
Deci, daca nu stiti ce sunt fisierele .tt, atunci cititi de la Hanselmann
http://www.hanselman.com/blog/T4TextTemplateTransformationToolkitCodeGenerationBestKeptVisualStudioSecret.aspx
si mergeti mai departe.
Puteti downloada solutia de la http://pmkb.codeplex.com/
Rezumat
Fisierele .tt va fac viata mai usoara, daca aveti definita o structura de baza. Folositi-le!