• 七色花基本权限系统(5) 实体配置的使用和利用T4自动生成实体配置


    在前面的章节里,用户表的结构非常简单,没有控制如何映射到数据库。通常,需要对字段的长度、是否可为空甚至特定数据类型进行设置,因为EntityFramework的默认映射规则相对而言比较简单和通用。在这篇日志里,将演示如何对数据实体进行映射配置,并利用T4模板自动创建映射配置类文件。

    配置方式

    EntityFramework的实体映射配置有2种。

    第一种是直接在实体类中以特性的方式进行控制,这些特性有部分是EF实现的,也有部分是非EF实现的。也就是说,在数据实体层不引用EF的情况下,只能使用不全的特性对实体映射进行控制。这种方式有2个明显的弊端:实体类没有尽可能地简单,数据实体层需要引用EF。

    第二种是通过EF提供的特定接口进行控制。这种控制方式将完全通过EF提供的配置API,能保证数据实体的干净和独立,并且将映射配置独立出来,便于维护。

    映射配置

    毫无疑问,这里将采用第二种配置方式。

    配置信息是要传递给EF数据上下文的,并且需要引用EF,因此实体配置可以直接建立在数据核心层(S.Framework.DataCore)中。在EntityFramework文件夹下建立新的文件夹,名称Configurations。

    需要提醒的是,在数据实体中,我们通过“不同名称的一级文件夹”对不同数据库的实体进行了隔离,在创建EF数据库上下文时,也用同样的名称作为前缀。因此,在实现实体配置时我们也需要进行区分,区分的方式也是文件夹隔离——在Configurations文件夹下创建文件夹,名称Master。如下图:

    image

    现在开始创建用户实体配置。

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 using System.Data.Entity.ModelConfiguration;
      7 using System.Data.Entity.ModelConfiguration.Configuration;
      8 
      9 using S.Framework.Entity.Master;
     10 
     11 namespace S.Framework.DataCore.EntityFramework.Configurations.Master
     12 {
     13     /// <summary>
     14     /// 数据表映射
     15     /// </summary>
     16     public class SysUserConfiguration : EntityTypeConfiguration<SysUser>
     17     {
     18         /// <summary>
     19         /// 数据表映射构造函数
     20         /// </summary>
     21         public SysUserConfiguration()
     22         {
     23             SysUserConfigurationAppend();
     24         }
     25 
     26         /// <summary>
     27         /// 数据映射
     28         /// </summary>
     29         public void SysUserConfigurationAppend()
     30         {
     31             //设置ID属性非空、最大长度38
     32             //由于EF会自动识别名为ID的属性作为主键(会自动非空),所以其实这里只需要设置最大长度即可
     33             this.Property(p => p.ID).IsRequired().HasMaxLength(38);
     34 
     35             //设置UserName属性非空、最大长度100
     36             this.Property(p => p.UserName).IsRequired().HasMaxLength(100);
     37 
     38             //设置Password属性非空、最大长度100
     39             this.Property(p => p.Password).IsRequired().HasMaxLength(100);
     40 
     41             //设置CreateUser属性非空、最大长度100
     42             this.Property(p => p.CreateUser).IsRequired().HasMaxLength(100);
     43 
     44             //设置LastModifyUser属性最大长度100
     45             this.Property(p => p.LastModifyUser).HasMaxLength(100);
     46         }
     47 
     48         /// <summary>
     49         /// 将映射配置注册给配置注册器
     50         /// </summary>
     51         /// <param name="configurations">配置注册器对象</param>
     52         public void RegistTo(ConfigurationRegistrar configurations)
     53         {
     54             configurations.Add(this);
     55         }
     56     }
     57 }
     58 
    用户实体配置类

    可配置的内容还有很多,比如数据精度、字段数据类型等等,请查API。

    这里稍微提几条常见的EF对实体属性映射的默认规则,这里以Sql server为例。

    值类型(int、bool等)的属性映射到数据库后,字段的数据类型是相应的int、bit等,且不允许为空;

    引用类型(好像只有string?)的属性映射到数据库后,字段的数据类型是nvarchar(max),允许为空;

    属性名为“ID”或者“实体类名ID”的属性,将自动被识别为主键;

    注册配置

    创建了实体配置类之后,还需要让EF数据库上下文知道有这个配置。

    在MasterEntityContext中的OnModelCreating方法中追加,使该方法变成这样:

      1 /// <summary>
      2 /// 模型配置重写
      3 /// </summary>
      4 /// <param name="modelBuilder">数据实体生成器</param>
      5 protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
      6 {
      7     // 禁用一对多级联删除
      8     modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
      9     // 禁用多对多级联删除
     10     modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
     11     // 禁用表名自动复数规则
     12     modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
     13 
     14     new SysUserConfiguration().RegistTo(modelBuilder.Configurations);
     15 }
    注册配置类给数据库上下文

    一切就绪,可以再创建一次数据库看看效果。

    删除原数据库,开启Home控制器下Index中的“测试访问数据库”的代码,运行。

    新生成的数据库结构如下图:

    image

    可以看到,字段的信息发生了改变。

    利用T4自动生成实体配置类

    当实体数量过多时,为各个实体创建配置类就变成了体力活。我们可以利用T4模板来偷懒,让它自动创建实体配置里。

    VS自带的T4模板,略有不足。请先安装2个关于T4的VS插件。

    工具 => 扩展和更新,联机搜索Devart T4 Editor和T4 Toolbox for Visual Studio 2013。

    image

    image

    需要注意的是,针对不同版本的VS,有不同版本的插件,请选择相应的版本。至于VS2015,听说没有这个插件,那我就不清楚了,还没用上VS2015。若有读者发现VS2015下这2个插件的变化还望留言告知。

    在EntityFramework文件夹下创建文件夹,名称T4,如下图:

    image

    装了2个插件之后,添加新项时会增加一个新的类型“T4 Toolbox”,我们将用Template文件创建模板,再用Script文件创建执行器。此处,我们先创建一个用于表示“实体配置类”的模板。如下图:

    image

    模板代码如下:

      1 <#+
      2 // <copyright file="Configuration.tt" company="">
      3 //  Copyright © . All Rights Reserved.
      4 // </copyright>
      5 public class Configuration : CSharpTemplate
      6 {
      7     private string _modelName; //实体名称
      8     private string _prefixName; //实体前缀名称,表示不同的数据库
      9     public Configuration(string modelName, string prefixName)
     10     {
     11         _modelName = modelName;
     12         _prefixName = prefixName;
     13     }
     14     public override string TransformText()
     15     {
     16     base.TransformText();
     17 #>
     18 using System;
     19 using System.Data.Entity.ModelConfiguration;
     20 using System.Data.Entity.ModelConfiguration.Configuration;
     21 using S.Framework.Entity.<#= _prefixName #>;
     22 namespace S.Framework.DataCore.EntityFramework.Configurations.<#= _prefixName #>
     23 {
     24     /// <summary>
     25     /// 数据表映射
     26     /// </summary>
     27     public class <#= _modelName #>Configuration : EntityTypeConfiguration<<#= _modelName #>>
     28     {
     29         /// <summary>
     30         /// 数据表映射构造函数
     31         /// </summary>
     32         public <#= _modelName #>Configuration()
     33         {
     34             <#= _modelName #>ConfigurationAppend();
     35         }
     36         /// <summary>
     37         /// 数据映射
     38         /// </summary>
     39         public void <#= _modelName #>ConfigurationAppend()
     40         {
     41         }
     42         /// <summary>
     43         /// 将映射配置注册给配置注册器
     44         /// </summary>
     45         /// <param name="configurations">配置注册器对象</param>
     46         public void RegistTo(ConfigurationRegistrar configurations)
     47         {
     48             configurations.Add(this);
     49         }
     50     }
     51 }
     52 <#+
     53     return this.GenerationEnvironment.ToString();
     54     }
     55 }
     56 #>
    实体配置模板文件

    T4不是重点,此处不做介绍,有兴趣的读者可以自行查阅资料学习。

    模板只是表示“目标文件的格式规则是怎样的”,还需要有一个“以一定条件去执行模板”的生成器。上面已经提到过Script执行器文件,创建一个吧。

    image

    执行器文件与模板文件有个明显可视的区别是:

    image

    执行器文件可以展开。模板文件的使命就是定义结构和规则,只要定义完成,就没它什么事情了。我们需要关注的是如何去调用模板文件。此处不多展开,直接放出Exec.tt文件代码。

      1 <#@ template language="C#" debug="True" #>
      2 <#@ output extension="cs" #>
      3 <#@ import namespace="System.IO" #>
      4 <#@ import namespace="System.Text" #>
      5 <#@ include file="T4Toolbox.tt" #>
      6 <#@ include file="Configuration.tt" #>
      7 <#
      8 
      9     string coreName = "S.Framework", projectName = coreName + ".DataCore";
     10     //当前完整路径
     11     string currentPath = Path.GetDirectoryName(Host.TemplateFile);
     12     //T4文件夹的父级文件夹路径
     13     string projectPath = currentPath.Substring(0, currentPath.IndexOf(@"\T4"));
     14     //解决方案路径
     15     string solutionFolderPath = currentPath.Substring(0, currentPath.IndexOf(@"\" + projectName));
     16 
     17     //实体名称和实体所在的数据库标识名称
     18     string modelName= "SysUser", prefixName= "Master";
     19     //目标文件的路径和名称(嵌套Generate文件夹是为了标识T4生成的类文件)
     20     string folderName= @"\Configurations\", fileName= prefixName + @"\Generate\" + modelName + "Configuration.cs";
     21     //执行实体配置模板,自动创建文件
     22     Configuration configuration = new Configuration(modelName, prefixName);
     23     configuration.Output.Encoding = Encoding.UTF8;
     24     string path = projectPath + folderName + fileName;
     25     configuration.RenderToFile(path);
     26 #>
     27 
    模板执行器文件

    注意,这个文件只要保存就会自动执行,也可以通过右键来运行或调试。

    image

    在T4里写代码会有点不适应,智能提示、dll库等都是缺失得非常厉害,需要自己手动import相应的dll才行,有问题多调试,会很快搞定的。

    执行Exec.tt之后,我们会发现一个新文件被自动创建了,如下图:

    image

    这个自动生成的配置类,按照模板中定义的那样,与底下那个手动创建的配置类是一致的。先不急删除“底下那个手动创建的配置类”,因为此时还有几个问题需要解决。

    第一个问题:模板文件既然叫做模板,必然是通用于各实体类的。那么必然无法在模板中指定“每个字段的配置”,因为每个实体类的字段都不同的。这样就导致“自动生成的配置类中并不包含配置”,那怎么解决呢?

    第二个问题:在模板执行器文件中,硬编码的方式写死了“要生成的目标实体的名称SysUser”,那岂不是需要在为每个实体生成配置类的时候都需要手动调整代码?

    先解决第一个问题。C#中可以通过关键字partial来表示部分的意思,可以用在类中,也可以中在方法中。这个关键字就是解决第一个问题的方式。调整配置类的模板文件,修改2处代码。

    把配置类的修饰符public改成partial,再把数据映射方法ConfigurationAppend修改成:

      1 /// <summary>
      2 /// 数据映射
      3 /// </summary>
      4 partial void <#= _modelName #>ConfigurationAppend();
    修改后的数据映射方法

    重新执行exec.tt。

    这样,“T4生成的配置类”和“手动创建的配置类”就存在一部分重复的内容,重复的内容交给T4,在“手动创建的配置类”中只需关注不定的内容。修改“手动创建配置类”,只保留“部分方法即可”,如下:

      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.Entity.Master;
      8 
      9 namespace S.Framework.DataCore.EntityFramework.Configurations.Master
     10 {
     11     /// <summary>
     12     /// 数据表映射
     13     /// </summary>
     14     partial class SysUserConfiguration
     15     {
     16         /// <summary>
     17         /// 数据映射
     18         /// </summary>
     19         partial void SysUserConfigurationAppend()
     20         {
     21             //设置ID属性非空、最大长度38
     22             //由于EF会自动识别名为ID的属性作为主键(会自动非空),所以其实这里只需要设置最大长度即可
     23             this.Property(p => p.ID).IsRequired().HasMaxLength(38);
     24 
     25             //设置UserName属性非空、最大长度100
     26             this.Property(p => p.UserName).IsRequired().HasMaxLength(100);
     27 
     28             //设置Password属性非空、最大长度100
     29             this.Property(p => p.Password).IsRequired().HasMaxLength(100);
     30 
     31             //设置CreateUser属性非空、最大长度100
     32             this.Property(p => p.CreateUser).IsRequired().HasMaxLength(100);
     33 
     34             //设置LastModifyUser属性最大长度100
     35             this.Property(p => p.LastModifyUser).HasMaxLength(100);
     36         }
     37     }
     38 }
     39 
    手动创建的配置类

    为了目录结构上的统一,我们把调整文件夹为如下结构:

    image

    需要注意的是:配置类的命名空间必须是S.Framework.DataCore.EntityFramework.Configurations.Master,否则关键字partial就没有相应的作用。

    现在解决第二个问题。

    我们用反射来解决这个问题。思路其实很简单,反射数据实体层,获取每个实体类的名称和数据库标识名称,循环执行配置模板文件。

    为了能够准确表示“这个类是数据实体类”,我们添加一个“实体类必须要继承的实体基本类”,这样一来,只需要寻找“继承了实体基本类的类”就可以了。

    同时再增加一个“实体通用属性公共类”,用来定义通用的属性,比如创建人、创建时间等字段,减少每个实体类的代码量。

    这2个类,先定义在数据实体层根目录中,将来会再做调整。

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 
      7 namespace S.Framework.Entity
      8 {
      9     /// <summary>
     10     /// 所有实体模型必须直接或间接继承此类,T4模板中反射 Entity.dll 时将只识别此类的派生类为实体模型
     11     /// </summary>
     12     public abstract class EntityModelBaseForReflection
     13     {
     14     }
     15 }
     16 
    实体模型基本类
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 
      7 namespace S.Framework.Entity
      8 {
      9     public abstract class EntityBaseModel : EntityModelBaseForReflection
     10     {
     11         /// <summary>
     12         /// 获取或设置一个 <see cref="string"/> 值,该值表示实体对象的数据创建者。
     13         /// </summary>
     14         public virtual string CreateUser { get; set; }
     15 
     16         /// <summary>
     17         /// 获取或设置一个 <see cref="DateTime"/> 值,该值表示实体对象的数据创建时间。
     18         /// </summary>
     19         public virtual DateTime CreateDate { get; set; }
     20 
     21         /// <summary>
     22         /// 获取或设置一个 <see cref="string"/> 值,该值表示实体对象的数据最后修改者。
     23         /// </summary>
     24         public virtual string LastModifyUser { get; set; }
     25 
     26         /// <summary>
     27         /// 获取或设置一个 <see cref="DateTime"/> 值,该值表示实体对象的数据最后修改时间。
     28         /// </summary>
     29         public virtual DateTime? LastModifyDate { get; set; }
     30     }
     31 }
     32 
    实体模型通用类

    这2个类都加上了abstract,表示不能被实例化。

    现在,让SysUser实体继承EntityBaseModel,并且删除SysUser中的创建人、创建时间、最后修改人、最后修改时间这4个属性。

    然后修改Exec.tt,通过反射来获取实体信息。

      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 <#
     12 
     13     string coreName = "S.Framework", projectName = coreName + ".DataCore", entityProjectName = coreName + ".Entity";
     14     string entityBaseModelName = entityProjectName + ".EntityBaseModel";
     15     string entityBaseModelNameForReflection = entityProjectName + ".EntityModelBaseForReflection";
     16     //当前完整路径
     17     string currentPath = Path.GetDirectoryName(Host.TemplateFile);
     18     //T4文件夹的父级文件夹路径
     19     string projectPath = currentPath.Substring(0, currentPath.IndexOf(@"\T4"));
     20     //解决方案路径
     21     string solutionFolderPath = currentPath.Substring(0, currentPath.IndexOf(@"\" + projectName));
     22 
     23     //加载数据实体.dll
     24     string entityFilePath = string.Concat(solutionFolderPath, ("\\"+ entityProjectName +"\\bin\\Debug\\" + entityProjectName + ".dll"));
     25     byte[] fileData = File.ReadAllBytes(entityFilePath);
     26     Assembly assembly = Assembly.Load(fileData);
     27     //反射出实体类,不知道为啥此处不能成功判定“是否继承EntityModelBaseForReflection类”
     28     //因此只能通过名称比较的方式来判定
     29     IEnumerable<Type> modelTypes = assembly.GetTypes().Where(m => m.IsClass && !m.IsAbstract && (m.BaseType.FullName.Equals(entityBaseModelName) || m.BaseType.FullName.Equals(entityBaseModelNameForReflection)));
     30 
     31     //循环实体类
     32     List<string> prefixNames = new List<string>();
     33     foreach (Type item in modelTypes)
     34     {
     35         //找 实体文件夹 名称
     36         string tempNamespace= item.Namespace, nameSpaceWithoutProjectName = tempNamespace.Substring(entityProjectName.Length);
     37         if(nameSpaceWithoutProjectName.IndexOf(".") != 0 || nameSpaceWithoutProjectName.LastIndexOf(".") > 0)
     38         { continue; }
     39 
     40         //是否直接继承实体基本类
     41         bool purity = item.BaseType.FullName.Equals(entityBaseModelNameForReflection);
     42         //实体所在的数据库标识名称
     43         string targetName = nameSpaceWithoutProjectName.Substring(1);
     44         if(!prefixNames.Any(a => a == targetName)){ prefixNames.Add(targetName); }
     45         //目标文件的路径和名称(嵌套Generate文件夹是为了标识T4生成的类文件)
     46         string fileName= targetName + @"\Generate\" + item.Name + "Configuration.cs";
     47 
     48         //配置文件
     49         string folderName= @"\Configurations\";
     50         Configuration configuration = new Configuration(item.Name, targetName);
     51         configuration.Output.Encoding = Encoding.UTF8;
     52         string path = projectPath + folderName + fileName;
     53         configuration.RenderToFile(path);
     54     }
     55 #>
     56 
    执行器文件

    这里注意一下,由于是通过反射S.Framework.Entity/bin/下的S.Framework.Entity.dll来获取实体信息,因此当新增或移除实体,请先编译S.Framework.Entity,再来运行模板执行器。

    既然有了“实体通用属性类EntityBaseModel”,那么就可以确定:继承于EntityBaseModel的实体,都需要配置“创建人、创建时间、最后修改人、最后修改时间”这4个字段。那也加到模板中吧,减少手动要写的代码。

    修改Configuration.tt如下:

      1 <#+
      2 // <copyright file="Configuration.tt" company="">
      3 //  Copyright © . All Rights Reserved.
      4 // </copyright>
      5 
      6 public class Configuration : CSharpTemplate
      7 {
      8     private string _modelName; //实体名称
      9     private string _prefixName; //实体前缀名称,表示不同的数据库
     10     private bool _purity; //是否为纯净的实体,若为纯净则表示无任何额外属性,不纯净则表示包含“创建、最后修改”等额外属性
     11     public Configuration(string modelName, string prefixName, bool purity)
     12     {
     13         _modelName = modelName;
     14         _prefixName = prefixName;
     15         _purity = purity;
     16     }
     17 
     18 	public override string TransformText()
     19 	{
     20 		base.TransformText();
     21 #>
     22 
     23 using System;
     24 using System.Data.Entity.ModelConfiguration;
     25 using System.Data.Entity.ModelConfiguration.Configuration;
     26 
     27 using S.Framework.Entity.<#= _prefixName #>;
     28 
     29 namespace S.Framework.DataCore.EntityFramework.Configurations.<#= _prefixName #>
     30 {
     31 	/// <summary>
     32     /// 数据表映射
     33     /// </summary>
     34     partial class <#= _modelName #>Configuration : EntityTypeConfiguration<<#= _modelName #>>
     35     {
     36         /// <summary>
     37         /// 数据表映射构造函数
     38         /// </summary>
     39         public <#= _modelName #>Configuration()
     40         {
     41             <#= _modelName #>ConfigurationDefault();
     42             <#= _modelName #>ConfigurationAppend();
     43         }
     44 
     45         /// <summary>
     46         /// 默认的数据映射
     47         /// </summary>
     48         public void <#= _modelName #>ConfigurationDefault()
     49         {
     50 <#+
     51         if(!this._purity)
     52 {
     53 #>
     54             this.Property(p => p.CreateUser).IsRequired().HasMaxLength(100);
     55 
     56             this.Property(p => p.LastModifyUser).HasMaxLength(100);
     57 <#+
     58 }
     59 #>
     60         }
     61 
     62         /// <summary>
     63         /// 数据映射
     64         /// </summary>
     65         partial void <#= _modelName #>ConfigurationAppend();
     66 
     67         /// <summary>
     68         /// 将映射配置注册给配置注册器
     69         /// </summary>
     70         /// <param name="configurations">配置注册器对象</param>
     71         public void RegistTo(ConfigurationRegistrar configurations)
     72         {
     73             configurations.Add(this);
     74         }
     75     }
     76 }
     77 <#+
     78         return this.GenerationEnvironment.ToString();
     79 	}
     80 }
     81 #>
     82 
    修改后的配置模板文件

    同时,需要在Exec.tt中增加参数的传递。

      1 Configuration configuration = new Configuration(item.Name, targetName);

    修改为

      1 Configuration configuration = new Configuration(item.Name, targetName, purity);

    再运行Exec.tt,打开生成的用户配置类,检查正确与否。

    验收成果

    为了检验T4功能和配置功能,我们添加一个角色实体类。

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 
      7 namespace S.Framework.Entity.Master
      8 {
      9     /// <summary>
     10     /// 角色
     11     /// </summary>
     12     public class SysRole : EntityBaseModel
     13     {
     14         /// <summary>
     15         /// 主键
     16         /// </summary>
     17         public string ID { get; set; }
     18 
     19         /// <summary>
     20         /// 名称
     21         /// </summary>
     22         public string Name { get; set; }
     23 
     24         /// <summary>
     25         /// 排序号
     26         /// </summary>
     27         public int SortNumber { get; set; }
     28     }
     29 }
     30 
    角色实体

    注意命名空间!别忘记删除“分类文件夹名System”。

    编译S.Framework.Entity,然后去右键运行Exec.tt。得到以下结果:

    image

    如果要自定义一些配置,请在Customize文件夹中建立同名同命名空间的角色配置类,注意关键字partial。参照自定义用户配置类。

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 
      7 namespace S.Framework.DataCore.EntityFramework.Configurations.Master
      8 {
      9     partial class SysRoleConfiguration
     10     {
     11         partial void SysRoleConfigurationAppend()
     12         {
     13             this.Property(p => p.ID).IsRequired().HasMaxLength(100);
     14 
     15             this.Property(p => p.Name).IsRequired().HasMaxLength(100);
     16         }
     17     }
     18 }
     19 
    自定义角色配置类

    现在把新实体关联到EF数据库上下文中,在MasterEntityContext中增加属性:

      1 /// <summary>
      2 /// 角色
      3 /// </summary>
      4 public DbSet<SysRole> Roles { get; set; }

    同时在OnModelCreating方法中增加对角色配置类的注册:

      1 new SysRoleConfiguration().RegistTo(modelBuilder.Configurations);

    删除原数据库,重新跑一下项目,让EF再次创建数据库吧。

    下一章节,将实现EntityFramework自动合并/迁移/数据初始化。

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

  • 相关阅读:
    【NOIP2018PJ正式赛】摆渡车
    【NOIP2018PJ正式赛】龙虎斗
    【NOIP2018PJ正式赛】标题统计
    高精度除单精度
    关于输出的东东
    高精度乘单精度
    【NOIP2012模拟10.26】电影票
    【NOIP2012模拟10.26】雕塑
    【NOIP2012模拟10.26】火炬手
    【NOIP2016提高A组模拟9.7】千帆渡
  • 原文地址:https://www.cnblogs.com/matrixkey/p/5561300.html
Copyright © 2020-2023  润新知