新年首作-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)。

   新年伊始,祝大家春節愉快,萬事如意,謝謝大家的閱讀,麻煩大伙點一下推薦,再次謝謝大家。 ^_^


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM