【EF Core】使用外部 Model
<p>对于模型的配置,98.757%的情况下,我们使用“数据批注”特性类,或者 Fluent API (重写 DbContext 类的 OnModelCreating 方法)进行配置即可。但在少数情况下,可能会考虑在 DbContext 之外配置模型。比如:</p><ul>
<li>你的实体类和模型,以及 DbContext 派生不在一个程序集中;</li>
<li>你可以想在配置模型时做一些自己特有的扩展;</li>
<li>你希望所有 DbContext 的实例共享一个 Model 实例,这样不必在每次实例化上下文时都配置一次模型。</li>
</ul>
<p>话又说回来,其实就每次实例都配置一次模型也不用耗什么性能,除非实体很多,或特殊情况导致初始化较慢。</p>
<p>使用外部模型后,不仅可以把 DbContextOptions(选项)对象全局共享,连模型也顺便共享了。老周先介绍一下原理,比吃咸菜还简单。我们一般会使用 DbContextOptionsBuilder 类(不管是不是泛型版本)来构建 Options,其中,有一个 UseModel 方法,可以传递一个实现 IModel 接口的对象实例。对,就是模型对象。</p>
<p>于是,问题就聚焦在这个 IModel 接口上,咱们一般不会花十牛二虎之力自己实现 IModel 接口的,并且,EF Core 内部有实现类,叫 Model。虽然咱们可以访问此类,但从框架的角度看显然人家是不希望咱们在代码中使用它的。应用程序代码通过 ModelBuilder 类构建模型,再访问它的 Model 属性来获得模型实例的引用。你如果自己实现 IModel 接口,意义不大的,而且你还要花很多精力去重新实现 EF Core 的各部功能,才能与框架对接。</p>
<p>由 ModelBuilder 类相关 API 可以展现模型构建过程中的各种细节,即设计时模型。DbContext.Database.EnsureCreated 方法、迁移等功能在创建 / 修改数据库时都使用设计时的模型对象,毕竟其包含的元数据比较完整。</p>
<p>在预置约定的作用下,模型的设计时构建结束后,可以生成运行时模型。EnsureCreated 与迁移功能不使用运行时模型(使用了会报错)。但在数据查询、插入、删除、更新这些常规操作时是可以使用运行时模型的。你可以自己编写运行时模型,做法是继承 RuntimeModel 类(位于 Microsoft.EntityFrameworkCore.Metadata 命名空间)。不……过,这个其实也不用你去写的,dotnet-ef 命令行工具使用 dbcontext optimize 命令就可以帮我们生成代码了。为什么用工具生成而不动手去写呢,因为运行时模型的构造和设计时模型其实是相同的——描述实体的特征是相同的。通常我们通过 ModelBuilder 的API构建了模型,没有必要在 RuntimeModel 上又重复写一遍。所以贴心的微软给咱们准备了 ef 工具,代替我们做重复的工作。比如,迁移(Migration)的代码也是要描述实体到数据表的映射的(表名、列名等),这些咱们在 ModelBuilder 中就能做,也没有必要在 Migration 时又重写一番。</p>
<p>ef 工具生成的运行模型也可以传递给选项类的 UseModel 方法的,但文已提过,运行时模型是不能用来创建数据库和表的,只可用于增删改查。</p>
<p><span style="color: rgba(0, 0, 128, 1)"> (以下内容是重写的,可能与老周第一次写的内容有些不同,老周尽量按相同的思路写。因为中途去处理一下别的事情,回来发现电脑从睡眠中唤醒直接蓝屏了,草稿未保存,丢了。铭瑄的主板可能要背锅,以前用的 Acer 不会有这问题)</span></p>
<p> 顺便解释一下设计时模型和运行时模型的不同。设计时模型就是我们常写的配置模型的过程,先由框架执行所有预置的约定,自动识别能识别的东西。随后执行咱们自己写的配置代码,完事后生成只读的模型(不用改了)。之后对数据做查询、更改等操作就用最终生成的模型。运行时模型说简单点,就是把模型的置进行“硬编码”,里面有几个实体,哪个类的,类有几个属性。表、列、函数、存储过程映射了哪些。主键是谁,外键是谁,全部用明确的代码写出来。不用执行预置约定,不用自动识别,不用猜测……直接把模型的结构写死了。也就是说,运行时模型执行的代码较少,性能会好一些。注意,这里仅仅指 EF Core 初始化阶段,至于查询数据的过程不受影响(查询也可以用 dotnet-ef 工具生成预编译的查询,原理和生成运行时模型差不多,就是少执行一些代码)。</p>
<p>用 dotnet-ef 工具生成优化代码一般在你发现程序初始化很慢时才考虑,如果不影响性能,可以不优化。</p>
<p>扯远了,回到咱们的主题。由于配置模型过程需要预置约定集合,咱们也没必要自己重写这些功能。同时,预置约定不仅包括 EF Core 部分,各个数据库提供者(如 SQL Server)可能会加入自己特有的约定。所以,我们手动把预置约定添加到集合中也很麻烦的,幸好,贴心的微软又又又为咱们准备了一组静态方法,直接调用就能生成 ModelBuilder 实例返回,非常地方便。</p>
<p>这些静态方法是按数据库提供者分组的:</p>
<table style="height: 132px; width: 781px" border="1">
<tbody>
<tr>
<td><span style="color: rgba(0, 51, 102, 1)"><strong>数据库</strong></span></td>
<td><span style="color: rgba(0, 51, 102, 1)"><strong>命名空间</strong></span></td>
<td><span style="color: rgba(0, 51, 102, 1)"><strong>类</strong></span></td>
<td><span style="color: rgba(0, 51, 102, 1)"><strong>静态方法</strong></span></td>
</tr>
<tr>
<td>SQL Server</td>
<td>Microsoft.EntityFrameworkCore.Metadata.Conventions</td>
<td>SqlServerConventionSetBuilder</td>
<td>CreateModelBuilder</td>
</tr>
<tr>
<td>SQLite</td>
<td>Microsoft.EntityFrameworkCore.Metadata.Conventions</td>
<td>SqliteConventionSetBuilder</td>
<td>CreateModelBuilder</td>
</tr>
<tr>
<td>PostgreSQL</td>
<td>Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Conventions</td>
<td>NpgsqlConventionSetBuilder</td>
<td>CreateModelBuilder</td>
</tr>
</tbody>
</table>
<p> 这样一来,咱们配置外部模型就跟在 OnModelCreating 方法中一样了。</p>
<p>下面老周用一个示例让大伙伴们掌握使用方法。</p>
<p>第一步,写实体。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Ultraman
{
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 标识
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Uid { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 称号
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> required <span style="color: rgba(0, 0, 255, 1)">string</span> Nick { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 年龄
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Age { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 特征
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> Speciality Spec { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 特性
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Speciality
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Id { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 身高
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">decimal</span> Height { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 体重
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">decimal</span> Weight { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 飞行速度
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">decimal</span> FlightSpeed { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
}</span></pre>
</div>
<p>Ultraman 表示超人,Speciality 表示超人的某些特征,如身高、体重、飞行速度。</p>
<p>第二步,派生 DbContext 类。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> DemoDbContext : DbContext
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> DemoDbContext(DbContextOptions<DemoDbContext><span style="color: rgba(0, 0, 0, 1)"> options)
: </span><span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">(options)
{
}
</span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">public</span> DbSet<Ultraman> Ultramen { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span></span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">public</span> DbSet<Speciality> SpecialSet { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">; }</span>
}</span></pre>
</div>
<p>第三步,老周用一个 ModelHelper 类,公开静态的 BuildModel 方法。配置好模型后直接返回。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ModelHelper
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> IModel BuildModel()
{
<strong><span style="background-color: rgba(255, 255, 0, 1)">ModelBuilder builder </span></strong></span><strong><span style="background-color: rgba(255, 255, 0, 1)">=</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)"> SqliteConventionSetBuilder.CreateModelBuilder();</span></strong>
builder.Entity</span><Ultraman>(et =><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 主键</span>
et.HasKey(x => x.Uid).HasName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PK_ultra_id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 长度约束</span>
et.Property(x => x.Nick).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">25</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 表映射</span>
et.ToTable(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_ultras</span><span style="color: rgba(128, 0, 0, 1)">"</span>, tb =><span style="color: rgba(0, 0, 0, 1)">
{
tb.Property(d </span>=> d.Uid).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ultr_id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
tb.Property(d </span>=> d.Nick).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ultr_nick</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
tb.Property(x </span>=> x.Age).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ultr_age</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 关系:一对一</span>
et.HasOne(x =><span style="color: rgba(0, 0, 0, 1)"> x.Spec)
.WithOne()
.HasForeignKey</span><Ultraman>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">spec_id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.HasPrincipalKey</span><Speciality>(s =><span style="color: rgba(0, 0, 0, 1)"> s.Id)
.HasConstraintName(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FK_ultra_spec</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
});
builder.Entity</span><Speciality>(et =><span style="color: rgba(0, 0, 0, 1)">
{
et.HasKey(c </span>=> c.Id).HasName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PK_spid</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 表/列映射</span>
et.ToTable(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_spec</span><span style="color: rgba(128, 0, 0, 1)">"</span>, tb =><span style="color: rgba(0, 0, 0, 1)">
{
tb.Property(q </span>=> q.Id).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">sp_id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
tb.Property(q </span>=> q.Height).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">sp_height</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
tb.Property(q </span>=> q.FlightSpeed).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">sp_flightspeed</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 精度控制</span>
et.Property(k => k.FlightSpeed).HasPrecision(<span style="color: rgba(128, 0, 128, 1)">7</span>, <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
et.Property(m </span>=> m.Height).HasPrecision(<span style="color: rgba(128, 0, 128, 1)">5</span>, <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
et.Property(o </span>=> o.Weight).HasPrecision(<span style="color: rgba(128, 0, 128, 1)">3</span>, <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回模型</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)"> builder.Model.FinalizeModel();</span></strong>
}
}</span></pre>
</div>
<p>配置模型的过程相信大伙们都很熟了。上面代码两处关键:</p>
<p>1、调用 SqliteConventionSetBuilder.CreateModelBuilder 方法生成 ModelBuilder 实例。老周这次用的是 SQLite 数据库;</p>
<p>2、模型配置完后,通过 ModelBuilder 实例的 Model 属性来获取模型实例的引用。按照约定,应该调用模型的 FinalizeModel 方法,返回模型的最终形态(只读或 RuntimeModel)。</p>
<p>第四步,通过 Options 来配置数据库与模型相关参数,再传给 DbContext 的子类构造函数,就能达到全局共享选项和模型的目的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 生成外部模型</span>
<span style="background-color: rgba(255, 255, 153, 1)"><strong>IModel extModel =<span style="color: rgba(0, 0, 0, 1)"> ModelHelper.BuildModel();
</span></strong></span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印一下模型结构</span>
<span style="color: rgba(0, 0, 0, 1)">Console.WriteLine(<strong><span style="background-color: rgba(255, 255, 153, 1)">extModel.ToDebugString()</span></strong>);
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">------------------------------------------------</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 选项</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> options = <span style="color: rgba(0, 0, 255, 1)">new</span> DbContextOptionsBuilder<DemoDbContext><span style="color: rgba(0, 0, 0, 1)">()
.UseSqlite(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">data source=test.db</span><span style="color: rgba(128, 0, 0, 1)">"</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接字符串</span>
<strong><span style="background-color: rgba(255, 255, 153, 1)">.UseModel(extModel)</span></strong> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 重要:使用外部模型
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> .LogTo(log => Console.WriteLine(log)) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 日志</span>
.Options; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取构建的选项实例</span></pre>
</div>
<p>首先当然是调用咱们刚写好的静态方法生成模型实例,应用外部模型很TM简单的,只要调用 UseModel 方法,把模型实例传递进去就好了。</p>
<p>ToDebugString 方法可以在生成模型中各实体的详细信息,就像这样:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Model:
<span style="background-color: rgba(204, 255, 204, 1)">EntityType: Speciality</span>
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
FlightSpeed (</span><span style="color: rgba(0, 0, 255, 1)">decimal</span><span style="color: rgba(0, 0, 0, 1)">) Required
Height (</span><span style="color: rgba(0, 0, 255, 1)">decimal</span><span style="color: rgba(0, 0, 0, 1)">) Required
Weight (</span><span style="color: rgba(0, 0, 255, 1)">decimal</span><span style="color: rgba(0, 0, 0, 1)">) Required
Keys:
Id PK
<span style="background-color: rgba(204, 255, 204, 1)">EntityType: Ultraman</span>
Properties:
Uid (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Age (int) Required
Nick (</span><span style="color: rgba(0, 0, 255, 1)">string</span>) Required MaxLength(<span style="color: rgba(128, 0, 128, 1)">25</span><span style="color: rgba(0, 0, 0, 1)">)
spec_id (no field, int) Shadow Required FK Index
Navigations:
Spec (Speciality) ToPrincipal Speciality
Keys:
Uid PK
Foreign keys:
Ultraman {</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 128, 0, 1)">spec_id</span><span style="color: rgba(128, 0, 0, 1)">'</span>} -> Speciality {<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 128, 0, 1)">Id</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">} Unique Required Cascade ToPrincipal: Spec
Indexes:
spec_id Unique</span></pre>
</div>
<p>在创建数据库前,在调试阶段,我们可以打印这信息来检查一下由实体构建的模型(Code First)是否正确。</p>
<p>第五步,用上面的选项类初始化上下文对象,先创建数据库,并写入几条数据记录。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> c = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DemoDbContext(options))
{
</span><span style="background-color: rgba(204, 255, 204, 1)"><span style="color: rgba(0, 0, 255, 1)">bool</span> res =</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(204, 255, 204, 1)"> c.Database.EnsureCreated()</span>;
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (res)
{
c.Ultramen.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Nick </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">赛文</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
Age </span>= <span style="color: rgba(128, 0, 128, 1)">17000</span><span style="color: rgba(0, 0, 0, 1)">,
Spec </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Height </span>= <span style="color: rgba(128, 0, 128, 1)">40.0M</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 米</span>
Weight = <span style="color: rgba(128, 0, 128, 1)">35000.0M</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 吨</span>
FlightSpeed = <span style="color: rgba(128, 0, 128, 1)">7.0M</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 马赫</span>
<span style="color: rgba(0, 0, 0, 1)"> }
});
c.Ultramen.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Nick </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">爱迪</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
Age </span>= <span style="color: rgba(128, 0, 128, 1)">8000</span><span style="color: rgba(0, 0, 0, 1)">,
Spec </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Height </span>= <span style="color: rgba(128, 0, 128, 1)">50.0M</span><span style="color: rgba(0, 0, 0, 1)">,
Weight </span>= <span style="color: rgba(128, 0, 128, 1)">44000.0M</span><span style="color: rgba(0, 0, 0, 1)">,
FlightSpeed </span>= <span style="color: rgba(128, 0, 128, 1)">9.0M</span><span style="color: rgba(0, 0, 0, 1)">
}
});
c.Ultramen.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Nick </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">戴拿</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
Age </span>= <span style="color: rgba(128, 0, 128, 1)">22</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 飞鸟信年龄</span>
Spec = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Height </span>= <span style="color: rgba(128, 0, 128, 1)">55.0M</span><span style="color: rgba(0, 0, 0, 1)">,
Weight </span>= <span style="color: rgba(128, 0, 128, 1)">45000.0M</span><span style="color: rgba(0, 0, 0, 1)">,
FlightSpeed </span>= <span style="color: rgba(128, 0, 128, 1)">8.0M</span><span style="color: rgba(0, 0, 0, 1)">
}
});
c.Ultramen.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Nick </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">盖亚</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
Age </span>= <span style="color: rgba(128, 0, 128, 1)">20</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 高山我梦年龄</span>
Spec = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
Height </span>= <span style="color: rgba(128, 0, 128, 1)">50.0M</span><span style="color: rgba(0, 0, 0, 1)">,
Weight </span>= <span style="color: rgba(128, 0, 128, 1)">42000.0M</span><span style="color: rgba(0, 0, 0, 1)">,
FlightSpeed </span>= <span style="color: rgba(128, 0, 128, 1)">20.0M</span><span style="color: rgba(0, 0, 0, 1)">
}
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存</span>
<span style="color: rgba(0, 0, 0, 1)"> c.SaveChanges();
}
}</span></pre>
</div>
<p>第六步,把上面插入的记录查询出来。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> c = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DemoDbContext(options))
{
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{0,-5}{1,-7}{2,-7}{3,-7}{4,-10}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">名称</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">年龄</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">身高(米)</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">体重(吨)</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">飞行速度(马赫)</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-------------------------------------------------------</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> q = <strong><span style="background-color: rgba(255, 255, 0, 1)">c.Ultramen.Include(x =></span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)"> x.Spec).ToList()</span></strong>;
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (Ultraman um <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> q)
{
Console.WriteLine($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{um.Nick,-5}{um.Age,-10}{um.Spec.Height,-12}{um.Spec.Weight,-13}{um.Spec.FlightSpeed,-10}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<p>这里要高度注意:c.Ultramen 访问 Ultraman 集合时,与 Ultraman 一对一的 Speciality 实体并没有加载。此处我们需要查询整个关系的数据,所以得调用 Include 方法把 Speciality 集合的数据也 SELECT 出来。ToList 方法真正触发 SQL 语句的生成和发送到数据库执行(与 LINQ 一样的原理)。</p>
<p>如果你嫌调用 Include 方法麻烦,可以在配置模型时让其默认预加载。</p>
<div class="cnblogs_code">
<pre>builder.Entity<Ultraman>(et =><span style="color: rgba(0, 0, 0, 1)">
{
……
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 关系:一对一</span>
et.HasOne(x =><span style="color: rgba(0, 0, 0, 1)"> x.Spec)
.WithOne()
.HasForeignKey</span><Ultraman>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">spec_id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.HasPrincipalKey</span><Speciality>(s =><span style="color: rgba(0, 0, 0, 1)"> s.Id)
.HasConstraintName(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FK_ultra_spec</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
<strong><span style="background-color: rgba(255, 255, 0, 1)">et.Navigation(k </span></strong></span><strong><span style="background-color: rgba(255, 255, 0, 1)">=></span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)"> k.Spec).AutoInclude();</span></strong>
});</span></pre>
</div>
<p>如果导航属性是个集合,引用的记录比较多,还是不要自动 Include 了,手动的好一些。</p>
<p>示例查询结果如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">名称 年龄 身高(米) 体重(吨) 飞行速度(马赫)
</span>-------------------------------------------------------<span style="color: rgba(0, 0, 0, 1)">
赛文 </span><span style="color: rgba(128, 0, 128, 1)">17000</span> <span style="color: rgba(128, 0, 128, 1)">40.0</span> <span style="color: rgba(128, 0, 128, 1)">35000.0</span> <span style="color: rgba(128, 0, 128, 1)">7.0</span><span style="color: rgba(0, 0, 0, 1)">
爱迪 </span><span style="color: rgba(128, 0, 128, 1)">8000</span> <span style="color: rgba(128, 0, 128, 1)">50.0</span> <span style="color: rgba(128, 0, 128, 1)">44000.0</span> <span style="color: rgba(128, 0, 128, 1)">9.0</span><span style="color: rgba(0, 0, 0, 1)">
戴拿 </span><span style="color: rgba(128, 0, 128, 1)">22</span> <span style="color: rgba(128, 0, 128, 1)">55.0</span> <span style="color: rgba(128, 0, 128, 1)">45000.0</span> <span style="color: rgba(128, 0, 128, 1)">8.0</span><span style="color: rgba(0, 0, 0, 1)">
盖亚 </span><span style="color: rgba(128, 0, 128, 1)">20</span> <span style="color: rgba(128, 0, 128, 1)">50.0</span> <span style="color: rgba(128, 0, 128, 1)">42000.0</span> <span style="color: rgba(128, 0, 128, 1)">20.0</span></pre>
</div>
<p>使用 dotnet ef dbcontext optimize 命令生成的运行时模型也可以通过 UseModel 方法引用的,道理一样。但要注意,运行时的模型不能用来创建数据库的。</p>
<p>好了,今天就水到这里了。</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/tcjiaan/p/19054696
頁:
[1]