牛牛姥 發表於 2025-11-16 21:33:00

【EF Core】未定义实体类的数据库模型

<p>不知道大伙伴们有没有这样的想法:如果我不定义实体类,那 EF Core 能建模吗?能正常映射数据库吗?能正常增删改查吗?</p>
<p>虽然一般开发场景很少这么干,但有时候,尤其是数据库中的某些视图,就不太想给它定义实体类。好消息,EF Core 还真支持不定义实体类的。可是,你一定会疑惑了,不定义实体类,那还怎么面向对象呢?不急,咱们一个个去探寻真相。</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)"> MyDbContext : DbContext
{
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.UseSqlServer(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">server=...</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)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
    {
      EntityTypeBuilder ent </span>= <span style="background-color: rgba(255, 255, 0, 1)"><strong>modelBuilder.Entity(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Student</span><span style="color: rgba(128, 0, 0, 1)">"</span></strong></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)"><strong>)</strong></span>;
      </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)">      ent.HasNoKey();
    }
}</span></pre>
</div>
<p>这里我给数据库模型添加了一个叫 Student 的实体,我可没有定义对应的类。然后我们打印一下模型信息,看看能不能建模。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">internal</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
{
    </span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">using</span> <span style="color: rgba(0, 0, 255, 1)">var</span> context = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MyDbContext();
      </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(<span style="background-color: rgba(255, 255, 0, 1)">context.Model.ToDebugString()</span>);
    }
}</span></pre>
</div>
<p>运行一下,好家伙,你看,还真的能建模了。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Model:
EntityType: Student (Dictionary</span>&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;) CLR Type: <span style="background-color: rgba(255, 255, 0, 1)"><em><strong>Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;</strong></em></span> Keyless</pre>
</div>
<p>注意 CLR Type 后面的信息。这下懂了,当你不给 EF Core 提供实体类时,它会默认使用字典类型,Key 是字符串,Value 是 object 类型。</p>
<p>&nbsp;</p>
<p>我们继续验证,既然能建模了,那定义些属性,包括主键。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
{
    EntityTypeBuilder ent </span>= modelBuilder.Entity(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Student</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>
    ent.Property&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StuID</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
    ent.Property</span>&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span>).IsRequired(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
    ent.Property</span>&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">).IsRequired();
    ent.Property</span>&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</span><span style="color: rgba(128, 0, 0, 1)">"</span>).IsRequired(<span style="color: rgba(0, 0, 255, 1)">false</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>
    ent.HasKey(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StuID</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>Property 方法要指定属性的类型,因为没有对应的 CLR 属性,不然 EF 不知道这个属性是什么类型。这个其实就像影子属性。</p>
<p>现在再看看模型的信息。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Model:
EntityType: Student (Dictionary</span>&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;) CLR Type: Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;<span style="color: rgba(0, 0, 0, 1)">
    Properties:
      StuID (no field, </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Indexer Required PK AfterSave:Throw ValueGenerated.OnAdd
      Age (no field, </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Indexer Required
      Major (no field, </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) Indexer
      Name (no field, </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) Indexer Required
    Keys:
      StuID PK</span></pre>
</div>
<p>看来是没问题的。</p>
<p>&nbsp;</p>
<p>好,进入下一个验证阶段。我们用这个模型去创建数据库(这里我用的是 SQL Server)。</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)"> MyDbContext : DbContext
{
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.<span style="background-color: rgba(255, 255, 0, 1)">UseSqlServer(</span></span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">server=(localdb)\\MSSQLLocalDB;database=my_school</span><span style="color: rgba(128, 0, 0, 1)">"</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">);</span>
    }

    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
    {
      ……
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">internal</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
{
    </span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">using</span> <span style="color: rgba(0, 0, 255, 1)">var</span> context = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MyDbContext();
      ……
      </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)">      context.<span style="background-color: rgba(255, 255, 0, 1)">Database.EnsureCreated()</span>;
    }
}</span></pre>
</div>
<p>运行后,成功创建数据库,包含数据表 Student。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">TABLE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">dbo</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">Student</span><span style="color: rgba(255, 0, 0, 1)">]</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">StuID</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">INT</span>            <span style="color: rgba(255, 0, 255, 1)">IDENTITY</span> (<span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
    </span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">Age</span><span style="color: rgba(255, 0, 0, 1)">]</span>   <span style="color: rgba(0, 0, 255, 1)">INT</span>            <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
    </span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">Major</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span> (<span style="color: rgba(255, 0, 255, 1)">MAX</span>) <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
    </span><span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">Name</span><span style="color: rgba(255, 0, 0, 1)">]</span><span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span> (<span style="color: rgba(255, 0, 255, 1)">MAX</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
    </span><span style="color: rgba(0, 0, 255, 1)">CONSTRAINT</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">PK_Student</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">PRIMARY</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span> <span style="color: rgba(0, 0, 255, 1)">CLUSTERED</span> (<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">StuID</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">ASC</span><span style="color: rgba(0, 0, 0, 1)">)
);</span></pre>
</div>
<p>默认它使用了 Student 为表名。</p>
<p>&nbsp;</p>
<p>咱们的数据库上下文还少了一个 DbSet&lt;&gt; 属性,为了能访问数据,应当定义此属性。不过,这样定义是错误的。</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)"> MyDbContext : DbContext
{
      ……

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <strong><span style="background-color: rgba(255, 255, 0, 1)">DbSet&lt;Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;&gt; Students { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span></span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">; }</span></strong>
}</span></pre>
</div>
<p>虽然在运行的时候没有抛出异常,但是,Students 属性在 DbContext 初始化时,DbSet&lt;&gt; 没有被正确设置,即内部的&nbsp;InternalDbSet 类初始化失败。说白了这个 DbSet 类型的属性你不能正常访问。</p>
<p>什么原因?当我们通过 LINQ 查询时,由于&nbsp;DbSet 本身就是实现了&nbsp;IQueryable&lt;&gt; 接口的,因此,EntityQueryable 属性会被&nbsp;IEnumerable.GetEnumerator() 等方法访问。</p>
<p><img src="https://img2024.cnblogs.com/blog/367389/202511/367389-20251116115145488-248610516.png" alt="image" width="621" height="146" loading="lazy"></p>
<p>然后我们再看&nbsp;EntityQueryable 属性的代码。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> EntityQueryable&lt;TEntity&gt;<span style="color: rgba(0, 0, 0, 1)"> EntityQueryable
{
    </span><span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)">
    {
      <strong><span style="background-color: rgba(255, 255, 0, 1)">CheckState</span></strong>();

      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> NonCapturingLazyInitializer.EnsureInitialized(
            </span><span style="color: rgba(0, 0, 255, 1)">ref</span><span style="color: rgba(0, 0, 0, 1)"> field,
            </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">,
            </span><span style="color: rgba(0, 0, 255, 1)">static</span> internalSet =&gt;<span style="color: rgba(0, 0, 0, 1)"> internalSet.CreateEntityQueryable());
    }
}</span></pre>
</div>
<p>这里触发了CheckState 方法。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> CheckState()
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ReSharper disable once AssignmentIsFullyDiscarded</span>
    =&gt; _ = EntityType;</pre>
</div>
<p>CheckState 方法又访问了 EntityType 属性。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> IEntityType EntityType
{
    </span><span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (field != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> field;
      }

      field </span>= _entityTypeName != <span style="color: rgba(0, 0, 255, 1)">null</span>
            ?<span style="color: rgba(0, 0, 0, 1)"> _context.Model.FindEntityType(_entityTypeName)
            : _context.Model.FindEntityType(</span><span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TEntity));

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (field == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">if</span> (_context.Model.IsShared(<span style="color: rgba(0, 0, 255, 1)">typeof</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">(TEntity)))</span>
            {
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> InvalidOperationException(CoreStrings.InvalidSetSharedType(<span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TEntity).ShortDisplayName()));
            }

            ……

      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> field;
    }
}</span></pre>
</div>
<p>其他地方不用看,重点是发现中间有一段是判断实体的类型是否为共享类型。IsShared 方法是 Model 类中定义的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> <span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> IsShared( Type type)
    </span>=&gt; <span style="background-color: rgba(255, 255, 0, 1)">FindIsSharedConfigurationSource</span>(type) != <span style="color: rgba(0, 0, 255, 1)">null</span>
      || Configuration?.GetConfigurationType(type) ==<span style="color: rgba(0, 0, 0, 1)"> TypeConfigurationType.SharedTypeEntityType;


</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> ConfigurationSource?<span style="color: rgba(0, 0, 0, 1)"> FindIsSharedConfigurationSource(Type type)
    </span>=&gt; <span style="background-color: rgba(255, 255, 0, 1)">_sharedTypes.TryGetValue(type, <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span> existingTypes) ? existingTypes.ConfigurationSource : <span style="color: rgba(0, 0, 255, 1)">null</span></span>;</pre>
</div>
<p>_sharedTypes 是 Model 类的字段,从初始化时,它就把 Dictionary&lt;string, object&gt; 设定为共享类型。</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)">readonly</span> Type <span style="background-color: rgba(255, 255, 0, 1)">DefaultPropertyBagType</span> = <strong><span style="background-color: rgba(0, 204, 255, 1)"><span style="color: rgba(0, 0, 255, 1)">typeof</span>(Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(0, 204, 255, 1)">)</span></strong>;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> Dictionary&lt;Type, (ConfigurationSource ConfigurationSource, SortedSet&lt;EntityType&gt; Types)&gt; _sharedTypes =
      <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">new</span>() { { <span style="background-color: rgba(255, 204, 0, 1)"><em><strong>DefaultPropertyBagType</strong></em></span>, (ConfigurationSource.Explicit, <span style="color: rgba(0, 0, 255, 1)">new</span> SortedSet&lt;EntityType&gt;(TypeBaseNameComparer.Instance)) } };</span></pre>
</div>
<p>看到没,_sharedTypes 字段从 new 那一刻起,它里面就包含了&nbsp;Dictionary&lt;string, object&gt; 类型。</p>
<p>共享类型的特点是允许多个实体使用同一个 .NET 类,而字典类型就是默认的共享类型。而使用&nbsp;DbSet&lt;...&gt; Students { get; set; } 这种格式定义的属性,在 DbContext 类的内部,只用实体类的 Type 作为索引的 Key,没有命名。一旦 Type 是多个实体共享的类型,就破坏了唯一性。这时候必须同时用 Type 和 name 来标识 DbSet 才能做到唯一区分。看看 DbContext 类内部的 DbSet 列表是怎么缓存的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> Dictionary&lt;<strong><span style="background-color: rgba(255, 255, 0, 1)">(Type Type, <span style="color: rgba(0, 0, 255, 1)">string</span>? Name)</span></strong>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;? _sets;</pre>
</div>
<p>说白了,DbContext 类的内部是用一个字典来缓存 DbSet 的(看,字典类型真是好用,哪儿都能用得上它),其中 Key 是由两个值构成的:实体的类型 + 实体的名字。对于常见的实体类,因为类是唯一的,不共享的,所以,Name 的值可以忽略;而共享类型则不同,多个实体的 Type 是相同的,不用 Name 的话无法区分。所以,对于咱们这个未定义实体类的 Student 实体,只能调用 Set 方法来返回,并且要显式地指定一个名字,名字必须和模型中注册的实体名相同,否则,查询的时候还是找不到映射的。</p>
<p>废话了那么多,正确的属性定义应当是这样的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> DbSet&lt;Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;&gt; Students =&gt; <span style="background-color: rgba(255, 255, 0, 1)">Set&lt;Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Student</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</span>;</pre>
</div>
<p>&nbsp;咱们把表映射完善一下。</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
    {
      EntityTypeBuilder ent </span>= modelBuilder.Entity(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Student</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>
      ent.Property&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StuID</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                              .HasColumnName(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">f_stuid</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      ent.Property</span>&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                              .IsRequired(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
                              .HasMaxLength(</span><span style="color: rgba(128, 0, 128, 1)">15</span><span style="color: rgba(0, 0, 0, 1)">)
                              .HasColumnName(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">f_name</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      ent.Property</span>&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                              .IsRequired()
                              .HasColumnName(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">f_age</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      ent.Property</span>&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                              .IsRequired(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
                              .HasMaxLength(</span><span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">)
                              .HasColumnName(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">f_major</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>
      ent.HasKey(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StuID</span><span style="color: rgba(128, 0, 0, 1)">"</span>).HasName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PK_stu_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>
      ent.ToTable(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_students</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
    }</span></pre>
</div>
<p>然后创建的数据库是这样的。</p>
<p><img src="https://img2024.cnblogs.com/blog/367389/202511/367389-20251116173226582-2073122230.png" alt="image" width="280" height="303" loading="lazy"></p>
<p>逐渐对味了,这一关可以 pass 了。</p>
<p>&nbsp;</p>
<p>接下来咱们还要验证一下,增删改查是否可行。</p>
<p>A、先插入五条记录。</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="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Name:姓名;Age:年龄;Major:专业</span>
Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;[] newRecs =<span style="color: rgba(0, 0, 0, 1)">
[
    </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, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</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)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = <span style="color: rgba(128, 0, 128, 1)">19</span><span style="color: rgba(0, 0, 0, 1)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</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)">
    },
    </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, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</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)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = <span style="color: rgba(128, 0, 128, 1)">20</span><span style="color: rgba(0, 0, 0, 1)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</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)">
    },
    </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, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</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)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = <span style="color: rgba(128, 0, 128, 1)">20</span><span style="color: rgba(0, 0, 0, 1)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</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)">
    },
    </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, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</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)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = <span style="color: rgba(128, 0, 128, 1)">22</span><span style="color: rgba(0, 0, 0, 1)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</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)">
    },
    </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, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</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)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = <span style="color: rgba(128, 0, 128, 1)">18</span><span style="color: rgba(0, 0, 0, 1)">,
      [</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</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)">
    }
];

context.Students.AddRange(newRecs);
</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)">int</span> n =<span style="color: rgba(0, 0, 0, 1)"> context.SaveChanges();
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">已更新{0}条数据</span><span style="color: rgba(128, 0, 0, 1)">"</span>, n);</pre>
</div>
<p>结果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/367389/202511/367389-20251116175041596-1858015684.png" alt="image" width="348" height="119" loading="lazy"></p>
<p>&nbsp;</p>
<p>B、现在看一下数据更新。把 ID=3 的同学的专业改为“土狗工程”。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">int</span> n =<span style="color: rgba(0, 0, 0, 1)"> context.Students
      .Where(stu </span>=&gt; (<span style="color: rgba(0, 0, 255, 1)">int</span>)stu[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StuID</span><span style="color: rgba(128, 0, 0, 1)">"</span>] == <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">)
      .<strong><span style="background-color: rgba(255, 255, 0, 1)">ExecuteUpdate(setter </span></strong></span><strong><span style="background-color: rgba(255, 255, 0, 1)">=&gt; setter.SetProperty(s =&gt; s[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</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></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">))</span></strong>;
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">更新了{0}条记录</span><span style="color: rgba(128, 0, 0, 1)">"</span>, n);</pre>
</div>
<p>这个写法可能有大伙伴没看懂,老周解释一下。</p>
<p>首先,用 ExecuteUpdate 方法的好处是只生成一条 UPDATE 语句,提高了效率。如果你先查询 ID=3 的数据,然后修改其属性,然后再提交更改。那样 EF 会先生成 SELECT 语句,返回数据后,在内存中修改,再生成 UPDATE 语句,这样不太必要。</p>
<p>ExecuteUpdate 方法生成的 UPDATE 语句,它的 Where 子句是和 LINQ 查询匹配的,由于我们要改 id=3 的记录,所以要先调用 Where 方法,让 EF 记住有筛选条件,然后再调用 ExecuteUpdate 方法生成 SET 子句。</p>
<p>ExecuteUpdate 是扩展方法,这里我调用的以下重载:</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)">int</span> ExecuteUpdate&lt;TSource&gt;(<span style="color: rgba(0, 0, 255, 1)">this</span> IQueryable&lt;TSource&gt; source, Action&lt;UpdateSettersBuilder&lt;TSource&gt;&gt; setPropertyCalls)</pre>
</div>
<p>参数是一只委托,委托有个输入参数,类型是&nbsp;UpdateSettersBuilder&lt;TSource&gt;。UpdateSettersBuilder 类的功能是帮助框架生成更新语句的 SET 子句。即调用它的 SetProperty 方法给实体的属性赋值。为了保持高度的灵活性和可扩展性,SetProperty 方法采用表达树的方式传参。此处,我使用的是以下重载:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> UpdateSettersBuilder&lt;TSource&gt; SetProperty&lt;TProperty&gt;<span style="color: rgba(0, 0, 0, 1)">(
    Expression</span>&lt;Func&lt;TSource, TProperty&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> propertyExpression,
    TProperty valueExpression
)</span></pre>
</div>
<p>根据 C# 语句可以隐式转化为 Expression 的原则,propertyExpression 参数可以简化理解为&nbsp;Func&lt;TSource, TProperty&gt;,即你要告 EF 我要更新实体的哪个属性,比如,我要更新 Car.Speed 属性,那么就是 c =&gt; c.Speed,其中,c 是 Car 实例。不过,这里我们的 Student 实体是没有定义类的,它是个字典,但没关系,我们就告诉框架要更新哪个 Key 的值就好了。也就是&nbsp;s =&gt; s["Major"]。</p>
<p>第二个参数 valueExpression 就是属性的新的值。</p>
<p>所以,综合起来,整个 ExecuteUpdate 方法的调用就是</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">ExecuteUpdate(
   setter </span>=&gt; setter.SetProperty(s =&gt; s[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 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)">
));</span></pre>
</div>
<p>等等啊,各位,别忙着按【F5】,上面代码还有个问题,如果你直接运行,就会报类型映射失败的错误。问题就出在这个 Lambda 表达式:s =&gt; s["Major"]。我们还记得,它的类型是 Dictionary&lt;string, object&gt;,也就是说,s["Major"] 给表达式树处理引擎提供的类型是 object,而 Major 属性其实要的是 string。</p>
<p>怎么解决呢,简单啊,把它强制转换为 string 就完事了。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">ExecuteUpdate(
    setter </span>=&gt; setter.SetProperty(s =&gt; <strong><span style="background-color: rgba(255, 255, 0, 1)">(<span style="color: rgba(0, 0, 255, 1)">string</span>)</span></strong>s[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 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)">
));</span></pre>
</div>
<p>现在运行代码就不会出错了。生成的 SQL 语句如下:</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">UPDATE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>
      <span style="color: rgba(0, 0, 255, 1)">SET</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">f_major</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(0, 128, 0, 1)">@p</span>
      <span style="color: rgba(0, 0, 255, 1)">FROM</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">tb_students</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">AS</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>
      <span style="color: rgba(0, 0, 255, 1)">WHERE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">f_stuid</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(128, 0, 0, 1); font-weight: bold">3</span></pre>
</div>
<p>咱们验证一下,看到底改了没有。</p>
<p><img src="https://img2024.cnblogs.com/blog/367389/202511/367389-20251116182711320-1220542303.png" alt="image" width="354" height="126" loading="lazy"></p>
<p>嗯,已经改了。</p>
<p>&nbsp;</p>
<p>C、删除数据。现在咱们把 ID=3 的数据记录删除。为了提高效率,我们用 ExecuteDelete 方法。</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">int</span> n =<span style="color: rgba(0, 0, 0, 1)"> context.Students
             .Where(stu </span>=&gt; (<span style="color: rgba(0, 0, 255, 1)">int</span>)stu[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StuID</span><span style="color: rgba(128, 0, 0, 1)">"</span>] == <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">)
             .<strong><span style="background-color: rgba(255, 255, 0, 1)">ExecuteDelete</span></strong>();
Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">已删除{0}条记录</span><span style="color: rgba(128, 0, 0, 1)">"</span>, n);</pre>
</div>
<p>ExecuteDelete 方法不需要参数,因为生成 DELTE FROM ... 语句一般只要带个 WHERE 子句作为筛选条件就可以了。所以,要先调用 Where 方法做筛选,然后才调用 ExecuteDelete 方法。不然会把整个表的记录删除。</p>
<p>生成的 SQL 语句如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">DELETE</span> <span style="color: rgba(0, 0, 255, 1)">FROM</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>
      <span style="color: rgba(0, 0, 255, 1)">FROM</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">tb_students</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">AS</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>
      <span style="color: rgba(0, 0, 255, 1)">WHERE</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">f_stuid</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(128, 0, 0, 1); font-weight: bold">3</span></pre>
</div>
<p>运行代码后,ID=3 的记录就没了。</p>
<p><img src="https://img2024.cnblogs.com/blog/367389/202511/367389-20251116211242168-1812824703.png" alt="image" width="359" height="111" loading="lazy"></p>
<p><span style="color: rgba(255, 0, 0, 1); font-size: 16px"><strong>严重注意:凡是调用 ExecuteXXX 方法处理数据的,不要再调用 SaveChanges 方法了。ExecuteXXX 方法不需要跟踪实体更改,而是直接生成 SQL 发送到数据库执行的。&nbsp;</strong></span></p>
<p>&nbsp;</p>
<p>D、查询数据。就剩下最后一个操作了。咱们把所有记录查询出来,并输出到控制台。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> stuArr =<span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)"> context.Students.ToArray</span>();
</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> q = <span style="color: rgba(0, 0, 255, 1)">from</span> s <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> stuArr
      </span><span style="color: rgba(0, 0, 255, 1)">select</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">
      {
            StuID </span>= (<span style="color: rgba(0, 0, 255, 1)">int</span>)s[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StuID</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
            Name </span>= (<span style="color: rgba(0, 0, 255, 1)">string</span>)s[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Name</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
            Age </span>= (<span style="color: rgba(0, 0, 255, 1)">int</span>)s[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Age</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
            Major </span>= (<span style="color: rgba(0, 0, 255, 1)">string</span>)s[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Major</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)">foreach</span>(<span style="color: rgba(0, 0, 255, 1)">var</span> x <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)">{x.Name}({x.StuID})\t{x.Age}岁,{x.Major}专业</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>context.Students.ToArray 由于调用了 ToArray 扩展方法,所以查询会执行,从而触发翻译为 SQL 语句,并发送到数据库,返回查询结果。生成 SQL 如下:</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">SELECT</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">f_stuid</span><span style="color: rgba(255, 0, 0, 1)">]</span>, <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">f_age</span><span style="color: rgba(255, 0, 0, 1)">]</span>, <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">f_major</span><span style="color: rgba(255, 0, 0, 1)">]</span>, <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span>.<span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">f_name</span><span style="color: rgba(255, 0, 0, 1)">]</span>
      <span style="color: rgba(0, 0, 255, 1)">FROM</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">tb_students</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">AS</span> <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">t</span><span style="color: rgba(255, 0, 0, 1)">]</span></pre>
</div>
<p>控制台输出结果如下:</p>
<div class="cnblogs_code">
<pre>刘桂圆(<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)   19岁,人力资源管理裁员方向专业
方小同(</span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">)   20岁,调酒师专业
史地分(</span><span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">)   22岁,堪舆学专业
吴胜隆(</span><span style="color: rgba(128, 0, 128, 1)">5</span>)   18岁,行星科学专业</pre>
</div>
<p>在查询的时候,你也可以直接用 SQL 语句,例如查询 ID=1 的记录。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">int</span> <strong><span style="background-color: rgba(255, 255, 0, 1)">qid = <span style="color: rgba(128, 0, 128, 1)">1</span></span></strong><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">var</span> stuArr =<span style="color: rgba(0, 0, 0, 1)"> context
               .Students
               .FromSql($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">select * from tb_students where f_stuid = {<span style="background-color: rgba(255, 255, 0, 1)">qid</span>}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
               .ToArray();</span></pre>
</div>
<p>建议使用这种格式化的方式来组织 SQL 语句,而不用 Raw SQL 来拼接,因为用格式化字符串构建 SQL 语句默认使用参数化查询,可以避免特殊字符注入攻击,保证安全。</p>
<p>&nbsp;</p>
<p>经过咱们一系列验证,表明不定义类的实体也可以正常完成增删改查操作的。只是使用 Dictionary&lt;string, object&gt; 类型在书写查询的时候,Key 的名称容易写错。尤其是项目你做了一半,让后别人接盘,这种情况很容易把 Key 写错,导致各种错误。</p>
<p>因此,不定义实体类的方案可以使用,但不建议全用,可以仅在部分表或视图的映射中使用。</p>
<p>好了,今天咱们就水到这里了。</p><br><br>
来源:https://www.cnblogs.com/tcjiaan/p/19227431
頁: [1]
查看完整版本: 【EF Core】未定义实体类的数据库模型