新年首作-ELinq+T4模版引擎制作多文件实体代码生成器
关于代码生成器的文章网上已经多的不能太多了,在6年前我写过两篇文章介绍代码生成器的,一篇主要代码生成器的7种模型架构设计思想:也谈代码生成器,另外一篇再谈代码生成器介绍了基于其中一种模型架构的实践文章,现在回顾一下6年前的文章拿到现在其设计思想也从不过时,呵呵自大了。好了废话不多说,今天给大家分享一下利用ELinq内置的数据库元数据模型和T4 模版引擎制作多文件实体代码生成器。(ELinq:是一个轻量简单易用的开源Linq ORM数据访问组件,支持Nullable类型和枚举类型,支持根据实体类自动建库建表建关系,支持根据数据库通过T4模版自动生成实体代码,对Linq 的谓词提供了完美的支持,旨在让绝大部份的主流数据库都使用 Linq 来进行程序开发,让开发人员访问数据库从SQL中解放出来,易学易用上手快,配置简单,并且提供了源代码下载,方便定制。支持多数据库,目前支持 Access、SQLServer、SqlCE、 SQLite、MySQL、ORACLE,未来还会支持更多的数据库)
本文使用以下工具:
- VS2010 SP1
- T4 模版引擎
- ELinq 数据库元数据模型(借助ELinq强大的ORM框架,支持多种数据库)
- SqlServer 数据库
操作步骤
- 创建控制台应用程序 Demo
- 通过Nuget添加ELinq 的引用:在Nuget控制台中输入:install-package ELinq, Nuget会自动的将你引用的库的这个库的依赖文件添加到你的项目引用中.
- 配置SqlServer数据库连接信息。添加App.Config 文件,并添加如下的配置信
<add name="Northwind" connectionString="Data Source=.;Initial Catalog=northwind;Persist Security Info=True;User ID=sa;Password=" providerName="System.Data.SqlClient" />
-
编译项目,保证ELinq已经输出到Bin目录下,为T4 模版准备
- 添加T4 文件,MultipleOutputHelper.ttinclude,该文件的作用是:生成多文件,获取当前活动项目的信息(配置文件路径,项目路径,数据路径,AppConfig等)
View Code
<#@ template language="C#" debug="True" hostspecific="True" #> <#@ assembly name="System.Core" #><#@ assembly name="EnvDTE" #><#@ assembly name="System.Xml" #><#@ assembly name="System.Xml.Linq" #><#@ assembly name="System.Configuration" #> <#@ import namespace="System" #><#@ import namespace="System.CodeDom" #><#@ import namespace="System.CodeDom.Compiler" #><#@ import namespace="System.Collections.Generic" #><#@ import namespace="System.IO" #><#@ import namespace="System.Linq" #><#@ import namespace="System.Reflection" #><#@ import namespace="System.Text" #><#@ import namespace="System.Xml.Linq" #><#@ import namespace="Microsoft.VisualStudio.TextTemplating" #><#@ import namespace="System.Configuration" #> <#+ public ConnectionStringSettings GetConnectionStringSettings(string connectionStringName){ if (string.IsNullOrEmpty(connectionStringName)) throw new ArgumentNullException("connectionStringName"); var configFile = new ExeConfigurationFileMap(); configFile.ExeConfigFilename = GetConfigPath(); if (string.IsNullOrEmpty(configFile.ExeConfigFilename)) throw new ArgumentNullException("The project does not contain App.config or Web.config file."); var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None); var connSection = config.ConnectionStrings; var connectionStringSettings = connSection.ConnectionStrings[connectionStringName]; if(connectionStringSettings == null) throw new ArgumentNullException("connectionStringName not exists"); return connectionStringSettings; } public string GetConfigPath(){ EnvDTE.Project project = GetCurrentProject(); foreach (EnvDTE.ProjectItem item in project.ProjectItems){ if (item.Name.Equals("App.config", StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("Web.config", StringComparison.InvariantCultureIgnoreCase)) return GetProjectPath() + "\\" + item.Name; } return String.Empty; } public string GetProjectPath(){ EnvDTE.Project project = GetCurrentProject(); System.IO.FileInfo info = new System.IO.FileInfo(project.FullName); return info.Directory.FullName; } public string GetDataDirectory(){ EnvDTE.Project project = GetCurrentProject(); return System.IO.Path.GetDirectoryName(project.FileName) + "\\App_Data\\"; } public EnvDTE.Project GetCurrentProject(){ IServiceProvider _ServiceProvider = (IServiceProvider)Host; if (_ServiceProvider == null) throw new Exception("Host property returned unexpected value (null)"); EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte == null) throw new Exception("Unable to retrieve EnvDTE.DTE"); Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects; if (activeSolutionProjects == null) throw new Exception("DTE.ActiveSolutionProjects returned null"); EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0); if (dteProject == null) throw new Exception("DTE.ActiveSolutionProjects[0] returned null"); return dteProject; } // Manager class records the various blocks so it can split them up class Manager { private class Block { public String Name; public int Start, Length; public bool IncludeInDefault; } private Block currentBlock; private List<Block> files = new List<Block>(); private Block footer = new Block(); private Block header = new Block(); private ITextTemplatingEngineHost host; private StringBuilder template; protected List<String> generatedFileNames = new List<String>(); public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) { return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template); } public void StartNewFile(String name) { if (name == null) throw new ArgumentNullException("name"); CurrentBlock = new Block { Name = name }; } public void StartFooter(bool includeInDefault = true) { CurrentBlock = footer; footer.IncludeInDefault = includeInDefault; } public void StartHeader(bool includeInDefault = true) { CurrentBlock = header; header.IncludeInDefault = includeInDefault; } public void EndBlock() { if (CurrentBlock == null) return; CurrentBlock.Length = template.Length - CurrentBlock.Start; if (CurrentBlock != header && CurrentBlock != footer) files.Add(CurrentBlock); currentBlock = null; } public virtual void Process(bool split, bool sync = true) { if (split) { EndBlock(); String headerText = template.ToString(header.Start, header.Length); String footerText = template.ToString(footer.Start, footer.Length); String outputPath = Path.GetDirectoryName(host.TemplateFile); files.Reverse(); if (!footer.IncludeInDefault) template.Remove(footer.Start, footer.Length); foreach(Block block in files) { String fileName = Path.Combine(outputPath, block.Name); String content = headerText + template.ToString(block.Start, block.Length) + footerText; generatedFileNames.Add(fileName); CreateFile(fileName, content); template.Remove(block.Start, block.Length); } if (!header.IncludeInDefault) template.Remove(header.Start, header.Length); } } protected virtual void CreateFile(String fileName, String content) { if (IsFileContentDifferent(fileName, content)) File.WriteAllText(fileName, content); } public virtual String GetCustomToolNamespace(String fileName) { return null; } public virtual String DefaultProjectNamespace { get { return null; } } protected bool IsFileContentDifferent(String fileName, String newContent) { return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent); } private Manager(ITextTemplatingEngineHost host, StringBuilder template) { this.host = host; this.template = template; } private Block CurrentBlock { get { return currentBlock; } set { if (CurrentBlock != null) EndBlock(); if (value != null) value.Start = template.Length; currentBlock = value; } } private class VSManager: Manager { private EnvDTE.ProjectItem templateProjectItem; private EnvDTE.DTE dte; private Action<String> checkOutAction; private Action<IEnumerable<String>> projectSyncAction; public override String DefaultProjectNamespace { get { return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString(); } } public override String GetCustomToolNamespace(string fileName) { return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString(); } public override void Process(bool split, bool sync) { if (templateProjectItem.ProjectItems == null) return; base.Process(split, sync); if (sync) projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null)); } protected override void CreateFile(String fileName, String content) { if (IsFileContentDifferent(fileName, content)) { CheckoutFileIfRequired(fileName); File.WriteAllText(fileName, content); } } internal VSManager(ITextTemplatingEngineHost host, StringBuilder template) : base(host, template) { var hostServiceProvider = (IServiceProvider) host; if (hostServiceProvider == null) throw new ArgumentNullException("Could not obtain IServiceProvider"); dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte == null) throw new ArgumentNullException("Could not obtain DTE from host"); templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile); checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName); projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames); } private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) { var keepFileNameSet = new HashSet<String>(keepFileNames); var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>(); var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames(0)) + "."; foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) projectFiles.Add(projectItem.get_FileNames(0), projectItem); // Remove unused items from the project foreach(var pair in projectFiles) if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix)) pair.Value.Delete(); // Add missing files to the project foreach(String fileName in keepFileNameSet) if (!projectFiles.ContainsKey(fileName)) templateProjectItem.ProjectItems.AddFromFile(fileName); } private void CheckoutFileIfRequired(String fileName) { var sc = dte.SourceControl; if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName)) checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null)); } } } #>
1 <#@ template language="C#" debug="True" hostspecific="True" #> 2 <#@ assembly name="System.Core" 3 #><#@ assembly name="EnvDTE" 4 #><#@ assembly name="System.Xml" 5 #><#@ assembly name="System.Xml.Linq" 6 #><#@ assembly name="System.Configuration" #> 7 <#@ import namespace="System" 8 #><#@ import namespace="System.CodeDom" 9 #><#@ import namespace="System.CodeDom.Compiler" 10 #><#@ import namespace="System.Collections.Generic" 11 #><#@ import namespace="System.IO" 12 #><#@ import namespace="System.Linq" 13 #><#@ import namespace="System.Reflection" 14 #><#@ import namespace="System.Text" 15 #><#@ import namespace="System.Xml.Linq" 16 #><#@ import namespace="Microsoft.VisualStudio.TextTemplating" 17 #><#@ import namespace="System.Configuration" #> 18 <#+ 19 public ConnectionStringSettings GetConnectionStringSettings(string connectionStringName){ 20 if (string.IsNullOrEmpty(connectionStringName)) 21 throw new ArgumentNullException("connectionStringName"); 22 var configFile = new ExeConfigurationFileMap(); 23 configFile.ExeConfigFilename = GetConfigPath(); 24 if (string.IsNullOrEmpty(configFile.ExeConfigFilename)) 25 throw new ArgumentNullException("The project does not contain App.config or Web.config file."); 26 var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None); 27 var connSection = config.ConnectionStrings; 28 var connectionStringSettings = connSection.ConnectionStrings[connectionStringName]; 29 if(connectionStringSettings == null) 30 throw new ArgumentNullException("connectionStringName not exists"); 31 return connectionStringSettings; 32 } 33 public string GetConfigPath(){ 34 EnvDTE.Project project = GetCurrentProject(); 35 foreach (EnvDTE.ProjectItem item in project.ProjectItems){ 36 if (item.Name.Equals("App.config", StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("Web.config", StringComparison.InvariantCultureIgnoreCase)) 37 return GetProjectPath() + "\\" + item.Name; 38 } 39 return String.Empty; 40 } 41 public string GetProjectPath(){ 42 EnvDTE.Project project = GetCurrentProject(); 43 System.IO.FileInfo info = new System.IO.FileInfo(project.FullName); 44 return info.Directory.FullName; 45 } 46 public string GetDataDirectory(){ 47 EnvDTE.Project project = GetCurrentProject(); 48 return System.IO.Path.GetDirectoryName(project.FileName) + "\\App_Data\\"; 49 } 50 51 public EnvDTE.Project GetCurrentProject(){ 52 IServiceProvider _ServiceProvider = (IServiceProvider)Host; 53 if (_ServiceProvider == null) 54 throw new Exception("Host property returned unexpected value (null)"); 55 56 EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE)); 57 if (dte == null) 58 throw new Exception("Unable to retrieve EnvDTE.DTE"); 59 60 Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects; 61 if (activeSolutionProjects == null) 62 throw new Exception("DTE.ActiveSolutionProjects returned null"); 63 64 EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0); 65 if (dteProject == null) 66 throw new Exception("DTE.ActiveSolutionProjects[0] returned null"); 67 68 return dteProject; 69 } 70 // Manager class records the various blocks so it can split them up 71 class Manager { 72 private class Block { 73 public String Name; 74 public int Start, Length; 75 public bool IncludeInDefault; 76 } 77 78 private Block currentBlock; 79 private List<Block> files = new List<Block>(); 80 private Block footer = new Block(); 81 private Block header = new Block(); 82 private ITextTemplatingEngineHost host; 83 private StringBuilder template; 84 protected List<String> generatedFileNames = new List<String>(); 85 86 public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) { 87 return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template); 88 } 89 90 public void StartNewFile(String name) { 91 if (name == null) 92 throw new ArgumentNullException("name"); 93 CurrentBlock = new Block { Name = name }; 94 } 95 96 public void StartFooter(bool includeInDefault = true) { 97 CurrentBlock = footer; 98 footer.IncludeInDefault = includeInDefault; 99 } 100 101 public void StartHeader(bool includeInDefault = true) { 102 CurrentBlock = header; 103 header.IncludeInDefault = includeInDefault; 104 } 105 106 public void EndBlock() { 107 if (CurrentBlock == null) 108 return; 109 CurrentBlock.Length = template.Length - CurrentBlock.Start; 110 if (CurrentBlock != header && CurrentBlock != footer) 111 files.Add(CurrentBlock); 112 currentBlock = null; 113 } 114 115 public virtual void Process(bool split, bool sync = true) { 116 if (split) { 117 EndBlock(); 118 String headerText = template.ToString(header.Start, header.Length); 119 String footerText = template.ToString(footer.Start, footer.Length); 120 String outputPath = Path.GetDirectoryName(host.TemplateFile); 121 files.Reverse(); 122 if (!footer.IncludeInDefault) 123 template.Remove(footer.Start, footer.Length); 124 foreach(Block block in files) { 125 String fileName = Path.Combine(outputPath, block.Name); 126 String content = headerText + template.ToString(block.Start, block.Length) + footerText; 127 generatedFileNames.Add(fileName); 128 CreateFile(fileName, content); 129 template.Remove(block.Start, block.Length); 130 } 131 if (!header.IncludeInDefault) 132 template.Remove(header.Start, header.Length); 133 } 134 } 135 136 protected virtual void CreateFile(String fileName, String content) { 137 if (IsFileContentDifferent(fileName, content)) 138 File.WriteAllText(fileName, content); 139 } 140 141 public virtual String GetCustomToolNamespace(String fileName) { 142 return null; 143 } 144 145 public virtual String DefaultProjectNamespace { 146 get { return null; } 147 } 148 149 protected bool IsFileContentDifferent(String fileName, String newContent) { 150 return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent); 151 } 152 153 private Manager(ITextTemplatingEngineHost host, StringBuilder template) { 154 this.host = host; 155 this.template = template; 156 } 157 158 private Block CurrentBlock { 159 get { return currentBlock; } 160 set { 161 if (CurrentBlock != null) 162 EndBlock(); 163 if (value != null) 164 value.Start = template.Length; 165 currentBlock = value; 166 } 167 } 168 169 private class VSManager: Manager { 170 private EnvDTE.ProjectItem templateProjectItem; 171 private EnvDTE.DTE dte; 172 private Action<String> checkOutAction; 173 private Action<IEnumerable<String>> projectSyncAction; 174 175 public override String DefaultProjectNamespace { 176 get { 177 return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString(); 178 } 179 } 180 181 public override String GetCustomToolNamespace(string fileName) { 182 return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString(); 183 } 184 185 public override void Process(bool split, bool sync) { 186 if (templateProjectItem.ProjectItems == null) 187 return; 188 base.Process(split, sync); 189 if (sync) 190 projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null)); 191 } 192 193 protected override void CreateFile(String fileName, String content) { 194 if (IsFileContentDifferent(fileName, content)) { 195 CheckoutFileIfRequired(fileName); 196 File.WriteAllText(fileName, content); 197 } 198 } 199 200 internal VSManager(ITextTemplatingEngineHost host, StringBuilder template) 201 : base(host, template) { 202 var hostServiceProvider = (IServiceProvider) host; 203 if (hostServiceProvider == null) 204 throw new ArgumentNullException("Could not obtain IServiceProvider"); 205 dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE)); 206 if (dte == null) 207 throw new ArgumentNullException("Could not obtain DTE from host"); 208 templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile); 209 checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName); 210 projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames); 211 } 212 213 private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) { 214 var keepFileNameSet = new HashSet<String>(keepFileNames); 215 var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>(); 216 var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames(0)) + "."; 217 foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) 218 projectFiles.Add(projectItem.get_FileNames(0), projectItem); 219 220 // Remove unused items from the project 221 foreach(var pair in projectFiles) 222 if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix)) 223 pair.Value.Delete(); 224 225 // Add missing files to the project 226 foreach(String fileName in keepFileNameSet) 227 if (!projectFiles.ContainsKey(fileName)) 228 templateProjectItem.ProjectItems.AddFromFile(fileName); 229 } 230 231 private void CheckoutFileIfRequired(String fileName) { 232 var sc = dte.SourceControl; 233 if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName)) 234 checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null)); 235 } 236 } 237 } #>
- 添加T4文件:NorthwindContext.tt
View Code
<#@ template debug="True" hostspecific="True" language="C#" #> <#@ Include File="MultipleOutputHelper.ttinclude" #> <#@ Assembly Name="$(TargetDir)NLite.dll" #> <#@ Assembly Name="$(TargetDir)ELinq.dll" #> <#@ Assembly Name="System.Data" #> <#@ Import Namespace="NLite.Data" #> <#@ Import Namespace="NLite.Data.CodeGeneration" #> <#@ output extension=".cs" #> <# var @namespace = "NLite.Data.CodeGenerationDemo"; var connectionStringName = "Northwind"; var dbContextName = Host.TemplateFile.Split('\\')[Host.TemplateFile.Split('\\').Length - 1].TrimEnd('.', 't'); var connectionStringSetting = GetConnectionStringSettings(connectionStringName); var connectionString = connectionStringSetting.ConnectionString; var dbProviderName = connectionStringSetting.ProviderName; var dbConfiguration = DbConfiguration.Configure(connectionString, dbProviderName); var databaseSchema = dbConfiguration.Schema; var manager = Manager.Create(Host, GenerationEnvironment); manager.StartHeader(); #> using System; using System.Collections.Generic; using System.Linq; using NLite.Data; namespace <#= @namespace #> { <# manager.EndBlock(); #> using NLite.Reflection; public partial class <#= dbContextName #>:DbContext { //连接字符串名称:基于Config文件中连接字符串的配置 const string connectionStringName = "<#= connectionStringName #>"; //构造dbConfiguration 对象 static DbConfiguration dbConfiguration; static <#= dbContextName #>() { dbConfiguration = DbConfiguration .Configure(connectionStringName) .SetSqlLogger(() =>SqlLog.Debug) .AddFromAssemblyOf<<#= dbContextName #>>(t=>t.HasAttribute<TableAttribute>(false)) ; } public <#= dbContextName #>():base(dbConfiguration){} <#foreach (var tb in databaseSchema.Tables.Union(databaseSchema.Views)){#> public IDbSet<<#=NamingConversion.Default.ClassName(tb.TableName) #>> <#= NamingConversion.Default.QueryableName(tb.TableName) #> { get; private set; } <#}#> } <#foreach (var tb in databaseSchema.Tables){ manager.StartNewFile(NamingConversion.Default.ClassName(tb.TableName) + ".generated.cs");#> [Table("<#= tb.TableName #>")] public partial class <#= NamingConversion.Default.ClassName( tb.TableName) #> { <#foreach (var col in tb.PrimaryKeys){#> [Id("<#= col.ColumnName#>",IsDbGenerated=<#= col.IsGenerated.ToString().ToLower() #>)] public <#= NamingConversion.Default.DataType(col) #> <#= NamingConversion.Default.PropertyName(col.ColumnName) #> { get;set; } <#}#> <#foreach (var col in tb.Columns){#> [Column("<#= col.ColumnName#>")] public <#= NamingConversion.Default.DataType(col) #> <#= NamingConversion.Default.PropertyName(col.ColumnName) #> { get;set; } <#}#> <#foreach (var fk in tb.ForeignKeys){#> [ManyToOne(ThisKey="<#= NamingConversion.Default.PropertyName( fk.ThisKey.ColumnName) #>",OtherKey="<#= NamingConversion.Default.PropertyName(fk.OtherKey.ColumnName) #>")] public <#= NamingConversion.Default.ClassName(fk.OtherTable.TableName) #> <#= NamingConversion.Default.ManyToOneName(fk) #> { get;set; } <#}#> <#foreach (var fk in tb.Children){#> [OneToMany(ThisKey="<#= NamingConversion.Default.PropertyName( fk.ThisKey.ColumnName) #>",OtherKey="<#= NamingConversion.Default.PropertyName(fk.OtherKey.ColumnName) #>")] public IList<<#= NamingConversion.Default.ClassName(fk.OtherTable.TableName) #>> <#= NamingConversion.Default.QueryableName(fk.OtherTable.TableName) #> { get;set; } <#}#> } <# } manager.EndBlock(); foreach (var tb in databaseSchema.Views){ manager.StartNewFile(NamingConversion.Default.ClassName(tb.TableName) + ".generated.cs");#> [Table("<#= tb.TableName #>",Readonly=true)] public partial class <#= NamingConversion.Default.ClassName( tb.TableName) #> { <#foreach (var col in tb.Columns){#> [Column("<#= col.ColumnName#>")] public <#= col.Type.Name #> <#= NamingConversion.Default.PropertyName(col.ColumnName) #> { get;set; } <#}#> } <# } manager.EndBlock(); manager.StartFooter(); #> } <# manager.EndBlock(); manager.Process(true); #>
- 保存一下NorthwindContext.tt 文件,系统会自动创建实体、实体间的关系(一对多,多对一)和DbContext, 见下图
8. 运行代码查看
补充
1. ELinq 数据库Schema元数据结构图(只要配置好了DbConfiguration对象,只需要调用其Schema属性就会自动得到如下Schema数据
2. ELinq 还提供了表名到类名、列名到属性等转换约定,针对特殊情况可以自行定制
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NLite.Data.Schema; namespace NLite.Data.CodeGeneration { /// <summary> /// 命名约定 /// </summary> public static class NamingConversion { /// <summary> /// 缺省命名约定 /// </summary> public static readonly INamingConversion Default = new DefaultNamingConversion(); } /// <summary> /// 命名约定 /// </summary> public interface INamingConversion { /// <summary> /// 表名转集合名 /// </summary> /// <param name="tableName"></param> /// <returns></returns> string QueryableName(string tableName); /// <summary> /// 表名转类名 /// </summary> /// <param name="tableName"></param> /// <returns></returns> string ClassName(string tableName); /// <summary> /// 列名转字段名 /// </summary> /// <param name="columnName"></param> /// <returns></returns> string FieldName(string columnName); /// <summary> /// 列名属性名转 /// </summary> /// <param name="columnName"></param> /// <returns></returns> string PropertyName(string columnName); /// <summary> /// 得到类的数据类型 /// </summary> /// <param name="col"></param> /// <returns></returns> string DataType(IColumnSchema col); /// <summary> /// 得到外键对应的导航属性名称 /// </summary> /// <param name="fk"></param> /// <returns></returns> string ManyToOneName(IForeignKeySchema fk); } }
3. 默认情况下自定义DbContext名称和T4模版文件的名称一致
4. 如何修该代码生成的命名空间以及数据库配置
总结
最后附上Demo代码,大家可以修改一下配置文件,即可支持其它数据库的代码生成,也可以修改T4 模版DIY 自己的代码生成器!随后会发布基于Nuget安装方式的T4 模版代码生成器包,供大家方便使用、
技术支持:
- 官方网站
- Nuge 下载页面
- ORM组件 ELinq系列
- ORM组件 ELinq 更新日志
- ORM组件 ELinq 使用答疑
- 在我的博客留言,我会尽可能地抽时间来答复大家的问题。
- 加入 ELinq用户的 QQ群(271342583)。
新年伊始,祝大家春节愉快,万事如意,谢谢大家的阅读,麻烦大伙点一下推荐,再次谢谢大家。 ^_^