六月牛 發表於 2025-8-24 18:46:00

【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(选项)对象全局共享,连模型也顺便共享了。老周先介绍一下原理,比吃咸菜还简单。我们一般会使用&nbsp;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 与迁移功能不使用运行时模型(使用了会报错)。但在数据查询、插入、删除、更新这些常规操作时是可以使用运行时模型的。你可以自己编写运行时模型,做法是继承&nbsp;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)">&nbsp;(以下内容是重写的,可能与老周第一次写的内容有些不同,老周尽量按相同的思路写。因为中途去处理一下别的事情,回来发现电脑从睡眠中唤醒直接蓝屏了,草稿未保存,丢了。铭瑄的主板可能要背锅,以前用的 Acer 不会有这问题)</span></p>
<p>&nbsp;顺便解释一下设计时模型和运行时模型的不同。设计时模型就是我们常写的配置模型的过程,先由框架执行所有预置的约定,自动识别能识别的东西。随后执行咱们自己写的配置代码,完事后生成只读的模型(不用改了)。之后对数据做查询、更改等操作就用最终生成的模型。运行时模型说简单点,就是把模型的置进行“硬编码”,里面有几个实体,哪个类的,类有几个属性。表、列、函数、存储过程映射了哪些。主键是谁,外键是谁,全部用明确的代码写出来。不用执行预置约定,不用自动识别,不用猜测……直接把模型的结构写死了。也就是说,运行时模型执行的代码较少,性能会好一些。注意,这里仅仅指 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>&nbsp;这样一来,咱们配置外部模型就跟在 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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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)">&lt;summary&gt;</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)">&lt;/summary&gt;</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&lt;DemoDbContext&gt;<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&lt;Ultraman&gt; 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&lt;Speciality&gt; 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>第三步,老周用一个&nbsp;ModelHelper 类,公开静态的&nbsp;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>&lt;Ultraman&gt;(et =&gt;<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 =&gt; 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 =&gt; 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 =&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                tb.Property(d </span>=&gt; 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>=&gt; 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>=&gt; 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 =&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Spec)
                .WithOne()
                .HasForeignKey</span>&lt;Ultraman&gt;(<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>&lt;Speciality&gt;(s =&gt;<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>&lt;Speciality&gt;(et =&gt;<span style="color: rgba(0, 0, 0, 1)">
      {
            et.HasKey(c </span>=&gt; 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 =&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                tb.Property(q </span>=&gt; 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>=&gt; 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>=&gt; 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 =&gt; 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>=&gt; 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>=&gt; 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、调用&nbsp;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&lt;DemoDbContext&gt;<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 =&gt; 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>} -&gt; 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 =&gt;</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 实体并没有加载。此处我们需要查询整个关系的数据,所以得调用&nbsp; Include 方法把 Speciality 集合的数据也 SELECT 出来。ToList 方法真正触发 SQL 语句的生成和发送到数据库执行(与 LINQ 一样的原理)。</p>
<p>如果你嫌调用 Include 方法麻烦,可以在配置模型时让其默认预加载。</p>
<div class="cnblogs_code">
<pre>builder.Entity&lt;Ultraman&gt;(et =&gt;<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 =&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Spec)
      .WithOne()
      .HasForeignKey</span>&lt;Ultraman&gt;(<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>&lt;Speciality&gt;(s =&gt;<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)">=&gt;</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>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/tcjiaan/p/19054696
頁: [1]
查看完整版本: 【EF Core】使用外部 Model