七色花基本权限系统(9)- 中途整理和完善

不多说,完善一下。

完善数据核心层

在数据实体层(SF.Framework.Entity)中,我们以一级文件夹区分了不同数据库的数据实体,如下图:

image

也是因此,在数据核心层(SF.Framework.DataCore)中定义EF数据上下文(MasterEntityContext)时,特地以Master作为类前缀来进行区分,实体配置工厂(MasterConfigurationFactory)也是同样的处理。

观察这2个类,简单有规则,完全可以用T4来自动生成。反射数据实体层,获取每个数据实体的命名空间的最后的名称(比如从S.Framework.Entity.Master获取Master),作为前缀去生成EF数据库上下文类和实体配置工厂类。

就这么办。创建2个模板,分别作为EF数据库上下文模板和实体配置工厂模板。

image

数据库上下文模板:

  1 <#+
  2 // <copyright file="EntityContext.tt" company="">
  3 //  Copyright © . All Rights Reserved.
  4 // </copyright>
  5 
  6 public class EntityContext : CSharpTemplate
  7 {
  8     private string _prefixName;
  9 
 10     public EntityContext(string prefixName)
 11     {
 12         _prefixName = prefixName;
 13     }
 14 	public override string TransformText()
 15 	{
 16 		base.TransformText();
 17 #>
 18 
 19 using System;
 20 using System.Collections.Generic;
 21 using System.Linq;
 22 using System.Text;
 23 using System.Threading.Tasks;
 24 using System.Data.Entity;
 25 using System.Data.Entity.ModelConfiguration.Conventions;
 26 
 27 using S.Framework.DataCore.EntityFramework.ConfigurationFactories;
 28 
 29 namespace S.Framework.DataCore.EntityFramework.EntityContexts
 30 {
 31 	/// <summary>
 32     /// 数据库上下文
 33     /// </summary>
 34     public class <#= _prefixName #>EntityContext : System.Data.Entity.DbContext
 35     {
 36         /// <summary>
 37         /// 构造函数
 38         /// </summary>
 39         /// <param name="nameOrConnectionString">数据库名称或连接字符串。</param>
 40         public <#= _prefixName #>EntityContext(string nameOrConnectionString)
 41             : base(nameOrConnectionString)
 42         { }
 43 
 44         /// <summary>
 45         /// 模型配置重写
 46         /// </summary>
 47         /// <param name="modelBuilder">数据实体生成器</param>
 48         protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
 49         {
 50             // 禁用一对多级联删除
 51             modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
 52             // 禁用多对多级联删除
 53             modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
 54             // 禁用表名自动复数规则
 55             modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
 56 
 57             <#= _prefixName #>ConfigurationFactory.ConfigurationsInit(modelBuilder.Configurations);
 58         }
 59     }
 60 }
 61 <#+
 62         return this.GenerationEnvironment.ToString();
 63 	}
 64 }
 65 #>
 66 
数据库上下文模板

实体配置工厂模板:

  1 <#+
  2 // <copyright file="ConfigurationFactory.tt" company="">
  3 //  Copyright © . All Rights Reserved.
  4 // </copyright>
  5 
  6 public class ConfigurationFactory : CSharpTemplate
  7 {
  8     private string _prefixName;
  9 
 10     public ConfigurationFactory(string prefixName)
 11     {
 12         _prefixName = prefixName;
 13     }
 14 	public override string TransformText()
 15 	{
 16 		base.TransformText();
 17 #>
 18 
 19 using System;
 20 using System.Collections.Generic;
 21 using System.Linq;
 22 using System.Text;
 23 using System.Threading.Tasks;
 24 using System.Data.Entity.ModelConfiguration.Configuration;
 25 
 26 using S.Utilities;
 27 
 28 namespace S.Framework.DataCore.EntityFramework.ConfigurationFactories
 29 {
 30 	/// <summary>
 31     /// 实体配置工厂类
 32     /// </summary>
 33     public static class <#= _prefixName #>ConfigurationFactory
 34     {
 35         /// <summary>
 36         /// 同步对象
 37         /// </summary>
 38         private static readonly object sync = new object();
 39 
 40         /// <summary>
 41         /// 唯一实例
 42         /// </summary>
 43         private static IEnumerable<IEntityConfiguration> singleton;
 44 
 45         /// <summary>
 46         /// 实体配置
 47         /// </summary>
 48         private static IEnumerable<IEntityConfiguration> Configurations
 49         {
 50             get
 51             {
 52                 if (singleton == null)
 53                 {
 54                     lock (sync)
 55                     {
 56                         if (singleton == null)
 57                         {
 58                             var types = typeof(IEntityConfiguration).GetSubClass().Where(w => !w.IsAbstract && w.Namespace.EndsWith("<#= _prefixName #>"));
 59 
 60                             singleton = types.Select(m => Activator.CreateInstance(m) as IEntityConfiguration);
 61                         }
 62                     }
 63                 }
 64 
 65                 return singleton;
 66             }
 67         }
 68 
 69         /// <summary>
 70         /// 初始化实体模型生成器
 71         /// </summary>
 72         /// <param name="configurations">实体模型生成器</param>
 73         public static void ConfigurationsInit(ConfigurationRegistrar configurations)
 74         {
 75             foreach (var configuration in Configurations)
 76             {
 77                 configuration.RegistTo(configurations);
 78             }
 79         }
 80     }
 81 }
 82 <#+
 83         return this.GenerationEnvironment.ToString();
 84 	}
 85 }
 86 #>
 87 
实体配置工厂模板

设计好模板之后,调整一下执行器文件的代码,就可以了。修改后的执行器代码如下:

  1 <#@ template language="C#" debug="True" #>
  2 <#@ assembly name="System.Core" #>
  3 <#@ output extension="cs" #>
  4 <#@ import namespace="System.IO" #>
  5 <#@ import namespace="System.Text" #>
  6 <#@ import namespace="System.Reflection" #>
  7 <#@ import namespace="System.Linq" #>
  8 <#@ import namespace="System.Collections.Generic" #>
  9 <#@ include file="T4Toolbox.tt" #>
 10 <#@ include file="Configuration.tt" #>
 11 <#@ include file="ConfigurationFactory.tt" #>
 12 <#@ include file="EntityContext.tt" #>
 13 <#
 14 
 15     string coreName = "S.Framework", projectName = coreName + ".DataCore", entityProjectName = coreName + ".Entity";
 16     string entityBaseModelName = entityProjectName + ".EntityBaseModel";
 17     string entityBaseModelNameForReflection = entityProjectName + ".EntityModelBaseForReflection";
 18     //当前完整路径
 19     string currentPath = Path.GetDirectoryName(Host.TemplateFile);
 20     //T4文件夹的父级文件夹路径
 21     string projectPath = currentPath.Substring(0, currentPath.IndexOf(@"T4"));
 22     //解决方案路径
 23     string solutionFolderPath = currentPath.Substring(0, currentPath.IndexOf(@"" + projectName));
 24 
 25     //加载数据实体.dll
 26     string entityFilePath = string.Concat(solutionFolderPath, ("\"+ entityProjectName +"\bin\Debug\" + entityProjectName + ".dll"));
 27     byte[] fileData = File.ReadAllBytes(entityFilePath);
 28     Assembly assembly = Assembly.Load(fileData);
 29     //反射出实体类,不知道为啥此处不能成功判定“是否继承EntityModelBaseForReflection类”
 30     //因此只能通过名称比较的方式来判定
 31     IEnumerable<Type> modelTypes = assembly.GetTypes().Where(m => m.IsClass && !m.IsAbstract && (m.BaseType.FullName.Equals(entityBaseModelName) || m.BaseType.FullName.Equals(entityBaseModelNameForReflection)));
 32 
 33     //循环实体类
 34     List<string> prefixNames = new List<string>();
 35     foreach (Type item in modelTypes)
 36     {
 37         //找 实体文件夹 名称
 38         string tempNamespace= item.Namespace, nameSpaceWithoutProjectName = tempNamespace.Substring(entityProjectName.Length);
 39         if(nameSpaceWithoutProjectName.IndexOf(".") != 0 || nameSpaceWithoutProjectName.LastIndexOf(".") > 0)
 40         { continue; }
 41 
 42         //是否直接继承实体基本类
 43         bool purity = item.BaseType.FullName.Equals(entityBaseModelNameForReflection);
 44         //实体所在的数据库标识名称
 45         string targetName = nameSpaceWithoutProjectName.Substring(1);
 46         if(!prefixNames.Any(a => a == targetName)){ prefixNames.Add(targetName); }
 47         //目标文件的路径和名称(嵌套Generate文件夹是为了标识T4生成的类文件)
 48         string fileName= targetName + @"Generate" + item.Name + "Configuration.cs";
 49 
 50         //配置文件
 51         string folderName= @"Configurations";
 52         Configuration configuration = new Configuration(item.Name, targetName, purity);
 53         configuration.Output.Encoding = Encoding.UTF8;
 54         string path = projectPath + folderName + fileName;
 55         configuration.RenderToFile(path);
 56     }
 57 
 58     foreach(string prefixName in prefixNames)
 59     {
 60         //配置工厂文件
 61         string fileName = prefixName + "ConfigurationFactory.Generate.cs";
 62         ConfigurationFactory configurationFactory = new ConfigurationFactory(prefixName);
 63         configurationFactory.Output.Encoding = Encoding.UTF8;
 64         string path = string.Format(@"{0}ConfigurationFactories", projectPath) + fileName;
 65         configurationFactory.RenderToFile(path);
 66 
 67         //数据库上下文文件
 68         fileName = prefixName + "EntityContext.Generate.cs";
 69         EntityContext entityContext = new EntityContext(prefixName);
 70         entityContext.Output.Encoding = Encoding.UTF8;
 71         path = string.Format(@"{0}EntityContexts", projectPath) + fileName;
 72         entityContext.RenderToFile(path);
 73     }
 74 #>
 75 
执行器代码

运行一下执行器,发现EF数据库上下文和实体配置工厂都自动创建了。此时可以删除旧的EF数据库上下文和实体配置工厂。文档结构目录如下图:

image

编译,重新登录一下试试,没问题就好。

完善数据实现层

同样的,在数据实现层(S.Framework.DataAchieve)中,数据库初始化操作类(MasterDatabaseInitializer)也可以用相同的做法来自动生成。

创建2个模板文件,分别表示数据库初始化策略配置类模板和数据库初始化类模板,如下图:

image

数据库初始化策略配置模板:

  1 <#+
  2 // <copyright file="MigrateConfiguration.tt" company="">
  3 //  Copyright © . All Rights Reserved.
  4 // </copyright>
  5 
  6 public class MigrateConfiguration : CSharpTemplate
  7 {
  8     private string _prefixName;
  9 
 10     public MigrateConfiguration(string prefixName)
 11     {
 12         this._prefixName = prefixName;
 13     }
 14 
 15 	public override string TransformText()
 16 	{
 17 		base.TransformText();
 18 #>
 19 using System;
 20 using System.Collections.Generic;
 21 using System.Linq;
 22 using System.Text;
 23 using System.Threading.Tasks;
 24 using System.Data.Entity.Migrations;
 25 
 26 using S.Framework.DataCore.EntityFramework.EntityContexts;
 27 
 28 namespace S.Framework.DataAchieve.EntityFramework.Initializes
 29 {
 30 	/// <summary>
 31     /// <#= _prefixName #> 数据库迁移策略
 32     /// </summary>
 33     internal sealed class <#= _prefixName #>MigrateConfiguration : DbMigrationsConfiguration<<#= _prefixName #>EntityContext>
 34     {
 35         /// <summary>
 36         /// 构造方法
 37         /// </summary>
 38         public <#= _prefixName #>MigrateConfiguration()
 39         {
 40             //表示开启自动合并
 41             this.AutomaticMigrationsEnabled = true;
 42             //表示将导致数据丢失的合并也允许进行
 43             this.AutomaticMigrationDataLossAllowed = true;
 44         }
 45 
 46         /// <summary>
 47         /// 数据初始化
 48         /// </summary>
 49         /// <param name="context">数据库上下文</param>
 50         protected override void Seed(<#= _prefixName #>EntityContext context)
 51         {
 52             //<#= _prefixName #>DataInit.DataInit(context);
 53         }
 54     }
 55 }
 56 <#+
 57         return this.GenerationEnvironment.ToString();
 58 	}
 59 }
 60 #>
 61 
数据库初始化策略配置模板

数据库初始化模板:

  1 <#+
  2 // <copyright file="DatabaseInitializer.tt" company="">
  3 //  Copyright © . All Rights Reserved.
  4 // </copyright>
  5 
  6 public class DatabaseInitializer : CSharpTemplate
  7 {
  8     private string _prefixName;
  9 
 10     public DatabaseInitializer(string prefixName)
 11     {
 12         this._prefixName = prefixName;
 13     }
 14 
 15 	public override string TransformText()
 16 	{
 17 		base.TransformText();
 18 #>
 19 
 20 using System;
 21 using System.Collections.Generic;
 22 using System.Linq;
 23 using System.Text;
 24 using System.Threading.Tasks;
 25 using System.Data.Entity;
 26 
 27 using S.Framework.DataCore.EntityFramework.EntityContexts;
 28 
 29 namespace S.Framework.DataAchieve.EntityFramework.Initializes
 30 {
 31 	/// <summary>
 32     /// <#= _prefixName #> 数据库初始化操作类
 33     /// </summary>
 34     public class <#= _prefixName #>DatabaseInitializer
 35     {
 36         /// <summary>
 37         /// 设置数据库初始化策略
 38         /// </summary>
 39         /// <param name="migrate">是否合并(自动迁移)。若是,则会检查数据库是否存在,若不存在则创建,若存在则进行自动迁移。若否,则不进行初始化操作(这样能避开EF访问sys.databases检测数据库是否存在,项目稳定后可将参数设置为false)。</param>
 40         public void Initialize(bool migrate)
 41         {
 42             if (!migrate)
 43             {
 44                 System.Data.Entity.Database.SetInitializer<<#= _prefixName #>EntityContext>(null);
 45             }
 46             else
 47             {
 48                 System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<<#= _prefixName #>EntityContext, <#= _prefixName #>MigrateConfiguration>(true));
 49             }
 50         }
 51     }
 52 }
 53 <#+
 54         return this.GenerationEnvironment.ToString();
 55 	}
 56 }
 57 #>
 58 
数据库初始化模板

设计好模板之后,调整一下执行器文件的代码,就可以了。修改后的执行器代码如下:

  1 <#@ template language="C#" debug="True" #>
  2 <#@ assembly name="System.Core" #>
  3 <#@ output extension="cs" #>
  4 <#@ import namespace="System.IO" #>
  5 <#@ import namespace="System.Text" #>
  6 <#@ import namespace="System.Reflection" #>
  7 <#@ import namespace="System.Linq" #>
  8 <#@ import namespace="System.Collections.Generic" #>
  9 <#@ include file="T4Toolbox.tt" #>
 10 <#@ include file="Repository.tt" #>
 11 <#@ include file="MigrateConfiguration.tt" #>
 12 <#@ include file="DatabaseInitializer.tt" #>
 13 <#
 14 
 15     string coreName = "S.Framework", projectName = coreName + ".DataAchieve", entityProjectName = coreName + ".Entity";
 16     string entityBaseModelName = entityProjectName + ".EntityBaseModel";
 17     string entityBaseModelNameForReflection = entityProjectName + ".EntityModelBaseForReflection";
 18     //当前完整路径
 19     string currentPath = Path.GetDirectoryName(Host.TemplateFile);
 20     //T4文件夹的父级文件夹路径
 21     string projectPath = currentPath.Substring(0, currentPath.IndexOf(@"T4"));
 22     //解决方案路径
 23     string solutionFolderPath = currentPath.Substring(0, currentPath.IndexOf(@"" + projectName));
 24 
 25     //加载数据实体.dll
 26     string entityFilePath = string.Concat(solutionFolderPath, ("\"+ entityProjectName +"\bin\Debug\" + entityProjectName + ".dll"));
 27     byte[] fileData = File.ReadAllBytes(entityFilePath);
 28     Assembly assembly = Assembly.Load(fileData);
 29     //反射出实体类,不知道为啥此处不能成功判定“是否继承EntityModelBaseForReflection类”
 30     //因此只能通过名称比较的方式来判定
 31     IEnumerable<Type> modelTypes = assembly.GetTypes().Where(m => m.IsClass && !m.IsAbstract && (m.BaseType.FullName.Equals(entityBaseModelName) || m.BaseType.FullName.Equals(entityBaseModelNameForReflection)));
 32 
 33     //循环实体类
 34     Dictionary<string, List<Type>> prefixModelTypes = new Dictionary<string, List<Type>>();//存储[数据库标识名称]和[实体类型集合]
 35     foreach (Type item in modelTypes)
 36     {
 37         //找 实体文件夹 名称
 38         string tempNamespace= item.Namespace, nameSpaceWithoutProjectName = tempNamespace.Substring(entityProjectName.Length);
 39         if(nameSpaceWithoutProjectName.IndexOf(".") != 0 || nameSpaceWithoutProjectName.LastIndexOf(".") > 0)
 40         { continue; }
 41 
 42         //是否直接继承实体基本类
 43         bool purity = item.BaseType.FullName.Equals(entityBaseModelNameForReflection);
 44         //实体所在的数据库标识名称
 45         string targetName = nameSpaceWithoutProjectName.Substring(1);
 46         List<Type> temp;
 47         if(prefixModelTypes.TryGetValue(targetName, out temp))
 48         {
 49             temp.Add(item);
 50         }
 51         else
 52         {
 53             temp = new List<Type>{ item };
 54             prefixModelTypes.Add(targetName, temp);
 55         }
 56 
 57         //目标文件的路径和名称(嵌套Generate文件夹是为了标识T4生成的类文件)
 58         string fileName= targetName + @"Generate" + item.Name + "Repository.cs";
 59         //仓储文件
 60         string folderName= @"Repositories";
 61         Repository repository = new Repository(item.Name, targetName);
 62         repository.Output.Encoding = Encoding.UTF8;
 63         string path = projectPath + folderName + fileName;
 64         repository.RenderToFile(path);
 65     }
 66 
 67     foreach(KeyValuePair<string, List<Type>> item in prefixModelTypes)
 68     {
 69         //数据库初始化策略配置文件
 70         string fileName = "/"+ item.Key +"/" + item.Key + "MigrateConfiguration.Generate.cs";
 71         MigrateConfiguration mc = new MigrateConfiguration(item.Key);
 72         mc.Output.Encoding = Encoding.UTF8;
 73         string path = string.Format(@"{0}Initializes", projectPath) + fileName;
 74         mc.RenderToFile(path);
 75 
 76         //数据库初始化类文件
 77         fileName = "/"+ item.Key +"/" + item.Key + "DatabaseInitializer.Generate.cs";
 78         DatabaseInitializer temp = new DatabaseInitializer(item.Key);
 79         temp.Output.Encoding = Encoding.UTF8;
 80         path = string.Format(@"{0}Initializes", projectPath) + fileName;
 81         temp.RenderToFile(path);
 82     }
 83 #>
 84 
执行器文件

T4执行后的文件结构目录如下图:

image

其中的MasterDataInit.cs是手动创建的,用来个性化地往数据库中添加指定初始数据:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 
  7 using S.Framework.DataCore.EntityFramework.EntityContexts;
  8 using S.Framework.Entity.Master;
  9 
 10 namespace S.Framework.DataAchieve.EntityFramework.Initializes
 11 {
 12     internal class MasterDataInit
 13     {
 14         internal static void DataInit(MasterEntityContext context)
 15         {
 16             //判断admin是否存在,若存在,不新增
 17             var dbSet = context.Set<SysUser>();
 18             if (!dbSet.Any(a => a.UserName == "admin"))
 19             {
 20                 var entity = new SysUser { ID = Guid.NewGuid().ToString(), UserName = "admin", Password = "123456", CreateDate = DateTime.Now, CreateUser = "admin" };
 21                 //将用户对象附加给数据库上下文(这仅仅是内存级别的操作)
 22                 dbSet.Add(entity);
 23                 //数据库上下文保存更改(提交变更到数据库执行)
 24                 context.SaveChanges();
 25             }
 26         }
 27     }
 28 }
 29 
往数据库填充初始数据

然后还需要修改MasterMigrateConfiguration.Generate.cs,在Seed中调用一下MasterDataInit的方法即可。

编译,重新登录一下试试,没问题就好。

统一数据库配置信息

创建类库项目,名称S.Framework,定义一个数据库配置类(DatabaseConfig.cs):

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 
  7 using S.Utilities;
  8 
  9 namespace S.Framework
 10 {
 11     public class DatabaseConfig
 12     {
 13         /// <summary>
 14         /// web.config文件中数据库连接字符串的名称
 15         /// </summary>
 16         public static string ConnectionStringName = "matrixkey";
 17         /// <summary>
 18         /// 默认的数据库管道名称
 19         /// </summary>
 20         public static readonly string DefaultProviderName = "System.Data.SqlClient";
 21 
 22         private static string _connectioonString = string.Empty;
 23         private static string _providerName = string.Empty;
 24 
 25         /// <summary>
 26         /// 数据连接字符串
 27         /// </summary>
 28         public static string ConnectionString
 29         {
 30             get
 31             {
 32                 if (string.IsNullOrEmpty(_connectioonString))
 33                 {
 34                     string tempConnectionString = string.Empty, tempProviderName = string.Empty;
 35                     if (ConfigHelper.TryGetConnectionInfo(ConnectionStringName, out tempConnectionString, out tempProviderName))
 36                     {
 37                         _connectioonString = tempConnectionString;
 38                         _providerName = tempProviderName;
 39                     }
 40                 }
 41                 if (string.IsNullOrWhiteSpace(_connectioonString))
 42                 {
 43                     throw ExceptionHelper.ThrowConfigException("不存在名为“" + ConnectionStringName + "”的数据库连接信息,请检查web.config文件中的设置。");
 44                 }
 45                 return _connectioonString;
 46             }
 47         }
 48 
 49         /// <summary>
 50         /// 数据提供程序
 51         /// </summary>
 52         public static string ProviderName
 53         {
 54             get
 55             {
 56                 if (string.IsNullOrEmpty(_providerName))
 57                 {
 58                     _providerName = ConfigHelper.ProviderName(ConnectionStringName);
 59                 }
 60                 if (string.IsNullOrWhiteSpace(_providerName))
 61                 {
 62                     _providerName = DefaultProviderName;
 63                 }
 64                 return _providerName;
 65             }
 66         }
 67 
 68         /// <summary>
 69         /// 表示WEB应用程序在初次使用数据库时,是否自动合并/迁移数据库。该属性只在 <see cref="DataDriven"/> 值为 EntityFramework 时有效。
 70         /// 该属性配置项由 Web.config/configuration/appSettings/Database:Migrate 参数指定。
 71         /// </summary>
 72         public static bool IsMigrateDatabase
 73         {
 74             get
 75             {
 76                 string tempIsMigrateDatabase = string.Empty;
 77                 if (ConfigHelper.TryGetAppSettingInfo("Database:Migrate", out tempIsMigrateDatabase))
 78                 { }
 79                 return string.IsNullOrEmpty(tempIsMigrateDatabase) ? false : tempIsMigrateDatabase.ToBoolean(false);
 80             }
 81         }
 82     }
 83 }
 84 
数据库配置类

在Web.config/appSettings中增加配置参数:

  1 <!--表示WEB应用程序在初次使用数据库时,是否自动合并/迁移数据库。-->
  2 <add key="Database:Migrate" value="true" />

然后就是使用定义的配置。

让WebUI层引用S.Framework,然后把WebUI层中的Global.asax中的

  1 new MasterDatabaseInitializer().Initialize(true);

修改成

  1 new MasterDatabaseInitializer().Initialize(S.Framework.DatabaseConfig.IsMigrateDatabase);

让S.Framework.DataAchieve层引用S.Framework,然后修改BaseRepository.cs的构造方法,把

  1 this.Db = new MasterEntityContext("matrixkey");

修改成

  1 this.Db = new MasterEntityContext(S.Framework.DatabaseConfig.ConnectionStringName);

总结一下,这篇日志演示了3处整理:

1、利用T4自动生成EF数据库上下文和实体配置工厂

2、利用T4自动生成数据库初始化策略和初始化类

3、统一配置数据库参数

下个章节将演示仓储接口的创建。

截止本章节,项目源码下载:点击下载(存在百度云盘中)

原文地址:https://www.cnblogs.com/matrixkey/p/5590777.html