新年首作-ELinq+T4模版引擎制作多文件实体代码生成器

  关于代码生成器的文章网上已经多的不能太多了,在6年前我写过两篇文章介绍代码生成器的,一篇主要代码生成器的7种模型架构设计思想:也谈代码生成器,另外一篇再谈代码生成器介绍了基于其中一种模型架构的实践文章,现在回顾一下6年前的文章拿到现在其设计思想也从不过时,呵呵自大了。好了废话不多说,今天给大家分享一下利用ELinq内置的数据库元数据模型和T4 模版引擎制作多文件实体代码生成器。(ELinq:是一个轻量简单易用的开源Linq ORM数据访问组件,支持Nullable类型和枚举类型,支持根据实体类自动建库建表建关系,支持根据数据库通过T4模版自动生成实体代码,对Linq 的谓词提供了完美的支持,旨在让绝大部份的主流数据库都使用 Linq 来进行程序开发,让开发人员访问数据库从SQL中解放出来,易学易用上手快,配置简单,并且提供了源代码下载,方便定制。支持多数据库,目前支持 Access、SQLServer、SqlCE、  SQLite、MySQL、ORACLE,未来还会支持更多的数据库

  本文使用以下工具:

  1. VS2010 SP1
  2. T4 模版引擎
  3. ELinq 数据库元数据模型(借助ELinq强大的ORM框架,支持多种数据库)
  4. SqlServer 数据库

    操作步骤

  1.   创建控制台应用程序 Demo
  2.   通过Nuget添加ELinq 的引用:在Nuget控制台中输入:install-package ELinq, Nuget会自动的将你引用的库的这个库的依赖文件添加到你的项目引用中.
  3.   配置SqlServer数据库连接信息。添加App.Config 文件,并添加如下的配置信
    <add name="Northwind" connectionString="Data Source=.;Initial Catalog=northwind;Persist Security Info=True;User ID=sa;Password="
    providerName="System.Data.SqlClient" />

     

  4. 编译项目,保证ELinq已经输出到Bin目录下,为T4 模版准备

  5. 添加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 } #>
  6. 添加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);
    #>
  7. 保存一下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 模版代码生成器包,供大家方便使用、

技术支持:

  1. 官方网站
  2. Nuge 下载页面
  3. ORM组件 ELinq系列
  4. ORM组件 ELinq 更新日志
  5. ORM组件 ELinq 使用答疑
  6. 在我的博客留言,我会尽可能地抽时间来答复大家的问题。
  7. 加入 ELinq用户的 QQ群(271342583)。

   新年伊始,祝大家春节愉快,万事如意,谢谢大家的阅读,麻烦大伙点一下推荐,再次谢谢大家。 ^_^

posted @ 2013-02-15 17:48  风云  阅读(4861)  评论(15编辑  收藏  举报