Mercurial > pub > bltoolkit
diff Source/Templates/BLToolkit.ttinclude @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Templates/BLToolkit.ttinclude Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,643 @@ +<#@ assembly name="System.Core" #> +<#@ assembly name="System.Data" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.IO" #> +<# + AppDomain.CurrentDomain.AssemblyResolve += (_,args) => + { + foreach (var a in Assemblies) + if (args.Name.ToLower().IndexOf(a.Key.ToLower()) >= 0) + return System.Reflection.Assembly.LoadFile(a.Value); + + if (DataProviderAssembly != null && args.Name.Split(',')[0] == System.IO.Path.GetFileNameWithoutExtension(DataProviderAssembly)) + return System.Reflection.Assembly.LoadFile(DataProviderAssembly); + + return null; + }; +#><#+ + +static Dictionary<string,string> Assemblies = new Dictionary<string,string>(); + +static Action<GeneratedTextTransformation,string> WriteComment = (tt,s) => tt.WriteLine("//{0}", s); +static Action<GeneratedTextTransformation,string> WriteSummary = (tt,s) => +{ + if (!string.IsNullOrWhiteSpace(s)) + { + tt.WriteLine("/// <summary>"); + tt.WriteLine("/// {0}", s); + tt.WriteLine("/// </summary>"); + } +}; +static Action<GeneratedTextTransformation,string> WriteUsing = (tt,s) => tt.WriteLine("using {0};", s); +static Action<GeneratedTextTransformation,string> WriteBeginNamespace = (tt,s) => { tt.WriteLine("namespace {0}", s); tt.WriteLine("{"); }; +static Action<GeneratedTextTransformation> WriteEndNamespace = tt => tt.WriteLine("}"); +static Action<GeneratedTextTransformation,string,string> WriteBeginClass = (tt,cl,bc) => +{ + tt.Write("public partial class {0}", cl); + if (!string.IsNullOrEmpty(bc)) + tt.Write(" : {0}", bc); + tt.WriteLine(""); + tt.WriteLine("{"); +}; +static Action<GeneratedTextTransformation> WriteEndClass = tt => tt.WriteLine("}"); +static Func<string,string,string> MakeGenericType = (c,t) => string.Format("{0}<{1}>", c, t); +static Func<string,string> MakeType = t => t; + +delegate void WriteTablePropertyAction(GeneratedTextTransformation tt, string name, string pname, int maxlen, int maxplen, string desc); + +static WriteTablePropertyAction WriteTableProperty = (tt,name,pname,maxlen,maxplen,desc) => +{ + WriteSummary(tt,desc); + tt.WriteLine("public Table<{0}>{1} {2}{3} {{ get {{ return this.GetTable<{0}>();{1} }} }}", name, tt.LenDiff(maxlen, name), pname, tt.LenDiff(maxplen, pname)); +}; +static Action<GeneratedTextTransformation,string> WriteAttribute = (tt,a) => tt.Write("[{0}]", a); +static Action<GeneratedTextTransformation> WriteAttributeLine = tt => tt.WriteLine(""); + +static string ConnectionString; +static string ConnectionType; +static string DataProviderAssembly = null; + +string DatabaseName = null; +string DataContextName = null; +string Namespace = "DataModel"; +string BaseDataContextClass = "DbManager"; +string BaseEntityClass = null; +string OneToManyAssociationType = "IEnumerable<{0}>"; + +string OwnerToInclude = null; +string[] DatabaseQuote = null; + +bool RenderField = false; +bool RenderBackReferences = true; +bool RenderForeignKeys = true; + +bool IsMetadataLoaded; + +int MaxColumnTypeLen; +int MaxColumnMemberLen; + +static Action<GeneratedTextTransformation,Column,int[],string[]> RenderColumn = (tt,c,maxLens,attrs) => +{ + WriteSummary(tt,c.Description); + + if (maxLens.Sum() > 0) + { + if (attrs.Any(_ => _ != null)) + { + tt.Write("["); + + for (var i = 0; i < attrs.Length; i++) + { + if (attrs[i] != null) + { + tt.Write(attrs[i]); + tt.WriteSpace(maxLens[i] - attrs[i].Length); + + if (attrs.Skip(i + 1).Any(_ => _ != null)) + tt.Write(", "); + else if (maxLens.Skip(i + 1).Any(_ => _ > 0)) + tt.WriteSpace(2); + } + else if (maxLens[i] > 0) + { + tt.WriteSpace(maxLens[i]); + + if (maxLens.Skip(i + 1).Any(_ => _ > 0)) + tt.WriteSpace(2); + } + } + + tt.Write("] "); + } + else + { + tt.WriteSpace(maxLens.Sum() + (maxLens.Where(_ => _ > 0).Count() - 1) * 2 + 3); + } + } + + tt.Write("public {0}{1} {2}", c.Type, tt.LenDiff(tt.MaxColumnTypeLen, c.Type), c.MemberName); + + if (tt.RenderField) + { + tt.Write(";"); + if (c.ColumnType != null) + tt.Write(tt.LenDiff(tt.MaxColumnMemberLen, c.MemberName)); + } + else + tt.Write("{0} {{ get; set; }}", tt.LenDiff(tt.MaxColumnMemberLen, c.MemberName)); + + if (c.ColumnType != null) + { + tt.Write(" // {0}", c.ColumnType); + + if (c.Length != 0) + tt.Write("({0})", c.Length); + + if (c.Precision != 0) + { + if (c.Scale == 0) + tt.Write("({0})", c.Precision); + else + tt.Write("({0},{1})", c.Precision, c.Scale); + } + } + + tt.WriteLine(""); +}; + +static Action<GeneratedTextTransformation,ForeignKey> RenderForeignKey = (tt,key) => +{ + WriteComment(tt, " " + key.KeyName); + tt.WriteLine("[Association(ThisKey=\"{0}\", OtherKey=\"{1}\", CanBeNull={2})]", + string.Join(", ", (from c in key.ThisColumns select c.MemberName).ToArray()), + string.Join(", ", (from c in key.OtherColumns select c.MemberName).ToArray()), + key.CanBeNull ? "true" : "false"); + + if (key.Attributes.Count > 0) + { + WriteAttribute(tt, string.Join(", ", key.Attributes.Distinct().ToArray())); + WriteAttributeLine(tt); + } + + tt.Write("public "); + + if (key.AssociationType == AssociationType.OneToMany) + tt.Write(tt.OneToManyAssociationType, key.OtherTable.ClassName); + else + tt.Write(key.OtherTable.ClassName); + + tt.Write(" "); + tt.Write(key.MemberName); + + if (tt.RenderField) + tt.WriteLine(";"); + else + tt.WriteLine(" { get; set; }"); +}; + +static Action<GeneratedTextTransformation,Table, bool> RenderTable = (tt,t, renderForeignKeys) => +{ + WriteSummary(tt,t.Description); + + if (t.IsView) + { + WriteComment(tt, " View"); + } + + RenderTableAttributes(tt, t); + + WriteBeginClass(tt, t.ClassName, t.BaseClassName); + + tt.PushIndent("\t"); + + if (t.Columns.Count > 0) + { + tt.MaxColumnTypeLen = t.Columns.Values.Max(_ => _.Type.Length); + tt.MaxColumnMemberLen = t.Columns.Values.Max(_ => _.MemberName.Length); + + var maxLens = new int[] + { + t.Columns.Values.Max(_ => _.MemberName == _.ColumnName ? 0 : "MapField('')".Length + _.ColumnName.Length), + t.Columns.Values.Max(_ => _.IsNullable ? "Nullable".Length : _.IsIdentity ? "Identity".Length : 0), + t.Columns.Values.Max(_ => _.IsIdentity && _.IsNullable ? "Identity".Length : 0), + t.Columns.Values.Max(_ => _.IsPrimaryKey ? string.Format("PrimaryKey({0})", _.PKIndex).Length : 0), + t.Columns.Values.Max(_ => _.Attributes.Count == 0 ? 0 : string.Join(", ", _.Attributes.Distinct().ToArray()).Length), + }; + + foreach (var c in from c in t.Columns.Values orderby c.ID select c) + { + var attrs = new string[] + { + c.MemberName == c.ColumnName ? null : string.Format("MapField(\"{0}\")", c.ColumnName), + c.IsNullable ? "Nullable" : c.IsIdentity ? "Identity" : null, + c.IsIdentity && c.IsNullable ? "Identity" : null, + c.IsPrimaryKey ? string.Format("PrimaryKey({0})", c.PKIndex) : null, + c.Attributes.Count == 0 ? null : string.Join(", ", c.Attributes.Distinct().ToArray()), + }; + + RenderColumn(tt, c, maxLens, attrs); + } + } + + if (renderForeignKeys && t.ForeignKeys.Count > 0) + { + foreach (var key in t.ForeignKeys.Values.Where(k => tt.RenderBackReferences || k.BackReference != null)) + { + tt.WriteLine(""); + RenderForeignKey(tt, key); + } + } + + tt.PopIndent(); + WriteEndClass(tt); +}; + +static Action<GeneratedTextTransformation,Table> RenderTableAttributes = (tt,t) => +{ + if (t.Attributes.Count > 0) + { + WriteAttribute(tt, string.Join(", ", t.Attributes.Distinct().ToArray())); + WriteAttributeLine(tt); + } + + string tbl = "TableName("; + + if (!string.IsNullOrEmpty(tt.DatabaseName)) + tbl += string.Format("Database=\"{0}\", ", tt.DatabaseName); + + if (!string.IsNullOrEmpty(t.Owner)) + tbl += string.Format("Owner=\"{0}\", ", t.Owner); + + tbl += string.Format("Name=\"{0}\")", t.TableName); + + WriteAttribute(tt, tbl); + WriteAttributeLine(tt); +}; + +List<string> Usings = new List<string>() +{ + "System", + "BLToolkit.Data", + "BLToolkit.Data.Linq", + "BLToolkit.DataAccess", + "BLToolkit.Mapping", +}; + +static Action<GeneratedTextTransformation> RenderUsing = tt => +{ + var q = + from ns in tt.Usings.Distinct() + group ns by ns.Split('.')[0]; + + var groups = + (from ns in q where ns.Key == "System" select ns).Concat + (from ns in q where ns.Key != "System" orderby ns.Key select ns); + + foreach (var gr in groups) + { + foreach (var ns in from s in gr orderby s select s) + WriteUsing(tt, ns); + + tt.WriteLine(""); + } +}; + +Action<GeneratedTextTransformation> BeforeGenerateModel = _ => {}; +Action<GeneratedTextTransformation> AfterGenerateModel = _ => {}; + +Action<GeneratedTextTransformation> BeforeWriteTableProperty = _ => {}; +Action<GeneratedTextTransformation> AfterWriteTableProperty = _ => {}; + +void GenerateModel() +{ + if (ConnectionString != null) ConnectionString = ConnectionString.Trim(); + if (DataContextName != null) DataContextName = DataContextName. Trim(); + + if (string.IsNullOrEmpty(ConnectionString)) { Error("ConnectionString cannot be empty."); return; } + + if (string.IsNullOrEmpty(DataContextName)) + DataContextName = "DataContext"; + + LoadMetadata(); + + BeforeGenerateModel(this); + + WriteComment(this, "---------------------------------------------------------------------------------------------------"); + WriteComment(this, " <auto-generated>"); + WriteComment(this, " This code was generated by BLToolkit template for T4."); + WriteComment(this, " Changes to this file may cause incorrect behavior and will be lost if the code is regenerated."); + WriteComment(this, " </auto-generated>"); + WriteComment(this, "---------------------------------------------------------------------------------------------------"); + + RenderUsing(this); + + WriteBeginNamespace(this, Namespace); + PushIndent("\t"); + + WriteBeginClass(this, DataContextName, BaseDataContextClass); + + var tlist = (from t in Tables.Values orderby t.TableName select t).ToList(); + var maxlen = tlist.Max(_ => _.ClassName.Length); + var maxplen = tlist.Max(_ => (_.DataContextPropertyName ?? _.ClassName).Length); + + PushIndent("\t"); + + BeforeWriteTableProperty(this); + + foreach (var t in tlist) + WriteTableProperty(this, t.ClassName, t.DataContextPropertyName ?? t.ClassName, maxlen, maxplen, t.Description); + + AfterWriteTableProperty(this); + + PopIndent(); + + WriteEndClass(this); + + foreach (var t in tlist) + { + WriteLine(""); + RenderTable(this, t, RenderForeignKeys); + } + + PopIndent(); + WriteEndNamespace(this); + + AfterGenerateModel(this); +} + +string LenDiff(int max, string str) +{ + var s = ""; + + while (max-- > str.Length) + s += " "; + + return s; +} + +void WriteSpace(int len) +{ + while (len-- > 0) + Write(" "); +} + +List<T> CreateList<T>(T item) +{ + return new List<T>(); +} + +Func<System.Data.IDbConnection> GetConnectionObject = () => +{ + Type connType = null; + + if (DataProviderAssembly != null) + { + try + { + var assembly = System.Reflection.Assembly.LoadFile(DataProviderAssembly); + connType = assembly.GetType(ConnectionType) + ?? assembly.GetType(ConnectionType.Substring(0, ConnectionType.IndexOf(","))); + } + catch + { + } + } + + if (connType == null) + connType = Type.GetType(ConnectionType); + + return (System.Data.IDbConnection)Activator.CreateInstance(connType); +}; + +System.Data.IDbConnection GetConnection() +{ + var conn = GetConnectionObject(); + + conn.ConnectionString = ConnectionString; + conn.Open(); + + return conn; +} + +void LoadMetadata() +{ + if (IsMetadataLoaded) + return; + + IsMetadataLoaded = true; + + if (!string.IsNullOrEmpty(DataProviderAssembly) && !DataProviderAssembly.Contains(":") && DataProviderAssembly.Contains("..")) + { + try + { + string path = this.Host.ResolvePath(""); + DataProviderAssembly = Path.GetFullPath(Path.Combine(path, DataProviderAssembly)); + } + catch + { + } + } + + BeforeLoadMetadata(this); + LoadServerMetadata(); + + if (DatabaseQuote != null) + { + foreach (var t in Tables.Values) + { + t.TableName = string.Format("{1}{0}{2}", t.TableName, DatabaseQuote.FirstOrDefault(), DatabaseQuote.Skip(1).FirstOrDefault() ?? DatabaseQuote.FirstOrDefault()); + foreach (var c in t.Columns.Values) + { + c.ColumnName = string.Format("{1}{0}{2}", c.ColumnName, DatabaseQuote.FirstOrDefault(), DatabaseQuote.Skip(1).FirstOrDefault() ?? DatabaseQuote.FirstOrDefault()); + } + } + } + + foreach (var t in Tables.Values) + { + if (t.ClassName.Contains(" ")) + { + var ss = t.ClassName.Split(' ').Where(_ => _.Trim().Length > 0).Select(_ => char.ToUpper(_[0]) + _.Substring(1)); + t.ClassName = string.Join("", ss.ToArray()); + } + } + + foreach (var t in Tables.Values) + foreach (var key in t.ForeignKeys.Values.ToList()) + if (!key.KeyName.EndsWith("_BackReference")) + key.OtherTable.ForeignKeys.Add(key.KeyName + "_BackReference", key.BackReference = new ForeignKey + { + KeyName = key.KeyName + "_BackReference", + MemberName = key.MemberName + "_BackReference", + AssociationType = AssociationType.Auto, + OtherTable = t, + ThisColumns = key.OtherColumns, + OtherColumns = key.ThisColumns, + }); + + foreach (var t in Tables.Values) + { + foreach (var key in t.ForeignKeys.Values) + { + if (key.BackReference != null && key.AssociationType == AssociationType.Auto) + { + if (key.ThisColumns.All(_ => _.IsPrimaryKey)) + { + if (t.Columns.Values.Count(_ => _.IsPrimaryKey) == key.ThisColumns.Count) + key.AssociationType = AssociationType.OneToOne; + else + key.AssociationType = AssociationType.ManyToOne; + } + else + key.AssociationType = AssociationType.ManyToOne; + + key.CanBeNull = key.ThisColumns.All(_ => _.IsNullable); + } + } + } + + foreach (var t in Tables.Values) + { + foreach (var key in t.ForeignKeys.Values) + { + var name = key.MemberName; + + if (key.BackReference != null && key.ThisColumns.Count == 1 && key.ThisColumns[0].MemberName.ToLower().EndsWith("id")) + { + name = key.ThisColumns[0].MemberName; + name = name.Substring(0, name.Length - "id".Length); + + if (!t.ForeignKeys.Values.Select(_ => _.MemberName).Concat( + t.Columns. Values.Select(_ => _.MemberName)).Concat( + new[] { t.ClassName }).Any(_ => _ == name)) + { + name = key.MemberName;; + } + } + + if (name == key.MemberName) + { + if (name.StartsWith("FK_")) + name = name.Substring(3); + + if (name.EndsWith("_BackReference")) + name = name.Substring(0, name.Length - "_BackReference".Length); + + name = string.Join("", name.Split('_').Where(_ => _.Length > 0 && _ != t.TableName).ToArray()); + + if (name.Length > 0) + name = key.AssociationType == AssociationType.OneToMany ? PluralizeAssociationName(name) : SingularizeAssociationName(name); + } + + if (name.Length != 0 && + !t.ForeignKeys.Values.Select(_ => _.MemberName).Concat( + t.Columns. Values.Select(_ => _.MemberName)).Concat( + new[] { t.ClassName }).Any(_ => _ == name)) + { + key.MemberName = name; + } + } + } + + if (Tables.Values.SelectMany(_ => _.ForeignKeys.Values).Any(_ => _.AssociationType == AssociationType.OneToMany)) + Usings.Add("System.Collections.Generic"); + + var keyWords = new HashSet<string> + { + "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", + "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", + "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", + "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long", "new", + "null", "object", "operator", "out", "override", "params", "private", "protected", "public", "readonly", + "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "struct", "switch", + "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", + "using", "virtual", "volatile", "void", "while" + }; + + foreach (var t in Tables.Values) + { + if (keyWords.Contains(t.ClassName)) + t.ClassName = "@" + t.ClassName; + + if (keyWords.Contains(t.DataContextPropertyName)) + t.DataContextPropertyName = "@" + t.DataContextPropertyName; + + foreach (var col in t.Columns.Values) + if (keyWords.Contains(col.MemberName)) + col.MemberName = "@" + col.MemberName; + } + + + AfterLoadMetadata(this); +} + +Func<string,string> PluralizeAssociationName = _ => _ + "s"; +Func<string,string> SingularizeAssociationName = _ => _; + +Action<GeneratedTextTransformation> BeforeLoadMetadata = _ => {}; +Action<GeneratedTextTransformation> AfterLoadMetadata = _ => {}; + +Dictionary<string,Table> Tables = new Dictionary<string,Table>(); + +public partial class Table +{ + public string Owner; + public string TableName; + public string ClassName; + public string DataContextPropertyName; + public string BaseClassName; + public bool IsView; + public string Description; + public List<string> Attributes = new List<string>(); + + public Dictionary<string,Column> Columns = new Dictionary<string,Column>(); + public Dictionary<string,ForeignKey> ForeignKeys = new Dictionary<string,ForeignKey>(); +} + +public partial class Column +{ + public int ID; + public string ColumnName; // Column name in database + public string MemberName; // Member name of the generated class + public bool IsNullable; + public bool IsIdentity; + public string Type; // Type of the generated member + public string ColumnType; // Type of the column in database + public bool IsClass; + public DbType DbType; + public SqlDbType SqlDbType; + public long Length; + public int Precision; + public int Scale; + public string Description; + + public int PKIndex = -1; + public List<string> Attributes = new List<string>(); + + public bool IsPrimaryKey { get { return PKIndex >= 0; } } +} + +public enum AssociationType +{ + Auto, + OneToOne, + OneToMany, + ManyToOne, +} + +public partial class ForeignKey +{ + public string KeyName; + public string MemberName; + public Table OtherTable; + public List<Column> ThisColumns = new List<Column>(); + public List<Column> OtherColumns = new List<Column>(); + public List<string> Attributes = new List<string>(); + public bool CanBeNull = true; + public ForeignKey BackReference; + + private AssociationType _associationType = AssociationType.Auto; + public AssociationType AssociationType + { + get { return _associationType; } + set + { + _associationType = value; + + if (BackReference != null) + { + switch (value) + { + case AssociationType.Auto : BackReference.AssociationType = AssociationType.Auto; break; + case AssociationType.OneToOne : BackReference.AssociationType = AssociationType.OneToOne; break; + case AssociationType.OneToMany : BackReference.AssociationType = AssociationType.ManyToOne; break; + case AssociationType.ManyToOne : BackReference.AssociationType = AssociationType.OneToMany; break; + } + } + } + } +} + +#>