战机五代半 發表於 2025-6-29 22:31:00

【EF Core】DbContext是如何识别出实体集合的

<p>在开始之前说明一下,你不要指望阅读完本文后会得到光,就算得到光你也未必能变成迪迦。本文老周仅介绍原理,可以给部分大伙伴们解惑。</p>
<p>咱们都知道,在派生 DbContext 类时,集体类的集合用 DbSet&lt;TEntity&gt; 表示,而咱们最常用的方法是在 DbContext 的派生类中公开 DbSet&lt;TEntity&gt; 属性。但在实例化 DbContext 后,我们并未给这些属性赋值,就能查询数据了,那么,DbContext 类(包括其子类)是如何识别出这些公共属性并填充数据的?</p>
<p>好,主题已经打开,接下来老周就开始表演了。有大伙伴会说了:切,这个看看源码不就知道了。是的,但有些人天生懒啊,不想看,那老周帮你看。</p>
<p>首先,咱们要了解,DbContext 类是如何维护实体集合的?DbContext 类中有这么个字段声明:</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span> Dictionary&lt;(Type Type, <span style="color: rgba(0, 0, 255, 1)">string</span>? Name), <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;? _sets;</pre>
</div>
<p>这行代码老周严重希望你能看懂,看不懂会很麻烦的哟。这是一个字典类型,没错吧。然后,Key是啥类型,Value是啥类型?</p>
<p>Key:是一个二元元组,第一项为 Type 对象,第二项为字符串对象。type 指的是实体类的 Type,name 指的是你为这个实体集合分配的名字。有伙伴会问,我怎么给它命名,DbSet 实例又不是我创建的?不急,请看下文;</p>
<p>Value:猜得出来,这是与实体集合相关的实例,DbSet&lt;&gt;,实际类型是内部类&nbsp;InternalDbSet&lt;TEntity&gt;。这个后面咱们再说。</p>
<p>咱们先不去关心 DbSet&lt;TEntity&gt; 实例是怎么创建的(因为这里面要绕绕弯子),至少咱们知道:在DbContext上声明的实体集合是缓存到一个字典中的。而把集合实例添加到字典中的是一个名为&nbsp;GetOrAddSet 的方法。注意该方法是显示实现了&nbsp;IDbSetCache 接口的。看看这个接口的定义:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> IDbSetCache
{
   
    </span><span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> GetOrAddSet(IDbSetSource source, Type type);

   
    </span><span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> GetOrAddSet(
      IDbSetSource source,
      </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> entityTypeName,
       Type type);

   
    IEnumerable</span>&lt;<span style="color: rgba(0, 0, 255, 1)">object</span>&gt;<span style="color: rgba(0, 0, 0, 1)"> GetSets();
}</span></pre>
</div>
<p>IDbSetSource 接口的实现者就是跟创建 DbSet 实例有关的,咱们先忽略它。把注意放那两个重载方法&nbsp;GetOrAddSet 上,它的功能就是获取或者添加实体集合的引用。咱们看到,这两个重载的区别在:1、以Type为标识添加;2、以Type + name为标识添加。而 DbContext 类是显式实现了&nbsp;IDbSetCache 接口的,即咱们上面提到过的,就是把 DbSet 实例存到那个名为 _sets 的字典中。</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> IDbSetCache.GetOrAddSet(
      IDbSetSource source,
       Type type)
    {
      CheckDisposed();

      _sets </span>??=<span style="color: rgba(0, 0, 0, 1)"> [];

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!_sets.TryGetValue((type, <span style="color: rgba(0, 0, 255, 1)">null</span>), <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span> <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">))
      {
            </span><span style="color: rgba(0, 0, 255, 1)">set</span> = source.Create(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, type);
            <em><strong><span style="background-color: rgba(255, 255, 0, 1)">_sets[(type, </span></strong></em></span><em><strong><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">null</span>)] = <span style="color: rgba(0, 0, 255, 1)">set</span></span></strong></em><span style="color: rgba(0, 0, 0, 1)"><em><strong><span style="background-color: rgba(255, 255, 0, 1)">;</span></strong></em>
            _cachedResettableServices </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)">return</span> <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">;
    }


    </span><span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> IDbSetCache.GetOrAddSet(
      IDbSetSource source,
      </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> entityTypeName,
       Type type)
    {
      CheckDisposed();

      _sets </span>??=<span style="color: rgba(0, 0, 0, 1)"> [];

      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!_sets.TryGetValue((type, entityTypeName), <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span> <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">))
      {
            </span><span style="color: rgba(0, 0, 255, 1)">set</span> = source.Create(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, entityTypeName, type);
            <em><strong><span style="background-color: rgba(255, 255, 0, 1)">_sets[(type, entityTypeName)] </span></strong></em></span><em><strong><span style="background-color: rgba(255, 255, 0, 1)">= <span style="color: rgba(0, 0, 255, 1)">set</span></span></strong></em><span style="color: rgba(0, 0, 0, 1)"><em><strong><span style="background-color: rgba(255, 255, 0, 1)">;</span></strong></em>
            _cachedResettableServices </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)">return</span> <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">;
    }</span></pre>
</div>
<p>当添加的实体集合有名字时,字典的Key是由 type 和 entiyTypeName 组成;当集合不提供名字时,Key 就由 type 和 null 组成。</p>
<p>然后,DbContext 类公开一组重载方法,封装了 GetOrAddSet 方法的调用。</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> DbSet&lt;TEntity&gt; Set&lt; TEntity&gt;<span style="color: rgba(0, 0, 0, 1)">()
      </span><span style="color: rgba(0, 0, 255, 1)">where</span> TEntity : <span style="color: rgba(0, 0, 255, 1)">class</span>
      =&gt; (DbSet&lt;TEntity&gt;)((IDbSetCache)<span style="color: rgba(0, 0, 255, 1)">this</span>).<strong><span style="background-color: rgba(255, 255, 0, 1)">GetOrAddSet(DbContextDependencies.SetSource, <span style="color: rgba(0, 0, 255, 1)">typeof</span></span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">(TEntity))</span></strong>;


    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> DbSet&lt;TEntity&gt; Set&lt; TEntity&gt;(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> name)
      </span><span style="color: rgba(0, 0, 255, 1)">where</span> TEntity : <span style="color: rgba(0, 0, 255, 1)">class</span>
      =&gt; (DbSet&lt;TEntity&gt;)((IDbSetCache)<span style="color: rgba(0, 0, 255, 1)">this</span>).<strong><span style="background-color: rgba(255, 255, 0, 1)">GetOrAddSet(DbContextDependencies.SetSource, name, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(TEntity))</span></strong>;</pre>
</div>
<p>&nbsp;</p>
<p>根据这个逻辑,那么,咱们在继承 DbContext 类时,这样写也可以(假设实体类为 Student):</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> DbSet&lt;Student&gt; Students =&gt; Set&lt;Student&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>
      <span style="color: rgba(0, 0, 255, 1)">public</span> DbSet&lt;Student&gt; Students =&gt; Set&lt;Student&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">stu</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<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)">public</span> DbSet&lt;Student&gt; Students { <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>那 DbContext 类是怎么识别并调用 GetOrAddSet 方法的?</p>
<p>这就要用到另一个辅助——&nbsp;IDbSetInitializer,其实现类为&nbsp;DbSetInitializer。</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)"> DbSetInitializer : IDbSetInitializer
{
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IDbSetFinder _setFinder;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IDbSetSource _setSource;

   
    </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> DbSetInitializer(
      IDbSetFinder setFinder,
      IDbSetSource setSource)
    {
      _setFinder </span>=<span style="color: rgba(0, 0, 0, 1)"> setFinder;
      _setSource </span>=<span style="color: rgba(0, 0, 0, 1)"> setSource;
    }

   
    </span><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)">void</span><span style="color: rgba(0, 0, 0, 1)"> InitializeSets(DbContext context)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> <strong><span style="background-color: rgba(255, 255, 0, 1)">setInfo <span style="color: rgba(0, 0, 255, 1)">in</span> _setFinder.FindSets(context.GetType()).Where(p =&gt; p.Setter != <span style="color: rgba(0, 0, 255, 1)">null</span></span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">)</span></strong>)
      {
            setInfo.Setter</span>!<span style="color: rgba(0, 0, 0, 1)">.SetClrValueUsingContainingEntity(
                context,
                ((IDbSetCache)context).<strong><span style="background-color: rgba(255, 255, 0, 1)">GetOrAddSet(_setSource, setInfo.Type))</span></strong>;
      }
    }
}</span></pre>
</div>
<p>这个&nbsp;InitializeSets 方法就是在 DbContext 类的构造函数中调用的。</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> DbContext(DbContextOptions options)
    {
      ……
      
      ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
            <strong><span style="background-color: rgba(255, 255, 0, 1)">.GetRequiredService</span></strong></span><strong><span style="background-color: rgba(255, 255, 0, 1)">&lt;IDbSetInitializer&gt;</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">()</span>
            <span style="background-color: rgba(255, 255, 0, 1)">.InitializeSets(</span></strong></span><strong><span style="color: rgba(0, 0, 255, 1); background-color: rgba(255, 255, 0, 1)">this</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">)</span></strong>;

      EntityFrameworkMetricsData.ReportDbContextInitializing();
    }</span></pre>
</div>
<p>由于各种辅助类型间有依赖关系,因此,EF Core 内部其实也使用了服务容器技术来自动实例化。咱们回到上面 InitializeSets 方法的实现代码上。从源代码中我们看到,其实完成从 DbContext 的公共属性识别 DbSet&lt;&gt; 这一功能的是名为 IDbSetFinder 的组件,它的内部实现类为&nbsp;DbSetFinder。</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)"> DbSetFinder : IDbSetFinder
{
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ConcurrentDictionary&lt;Type, IReadOnlyList&lt;DbSetProperty&gt;&gt; _cache = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">virtual</span> IReadOnlyList&lt;DbSetProperty&gt;<span style="color: rgba(0, 0, 0, 1)"> FindSets(Type contextType)
      </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> _cache.GetOrAdd(contextType, FindSetsNonCached);

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> DbSetProperty[] FindSetsNonCached(Type contextType)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> factory =<span style="color: rgba(0, 0, 0, 1)"> ClrPropertySetterFactory.Instance;

      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> contextType.GetRuntimeProperties()
</span>            <strong><span style="background-color: rgba(255, 255, 0, 1)">.Where(
</span></strong>                <strong><span style="background-color: rgba(255, 255, 0, 1)">p </span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">=&gt; !</span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&amp;&amp; !</span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&amp;&amp; p.DeclaringType != <span style="color: rgba(0, 0, 255, 1)">typeof</span></span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&amp;&amp;</span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&amp;&amp; p.PropertyType.GetGenericTypeDefinition() == <span style="color: rgba(0, 0, 255, 1)">typeof</span>(DbSet&lt;&gt;</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">))</span></strong>
            .OrderBy(p </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> p.Name)
            .Select(
                p </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DbSetProperty(
                  p.Name,
                  p.PropertyType.GenericTypeArguments.Single(),
                  p.SetMethod </span>== <span style="color: rgba(0, 0, 255, 1)">null</span> ? <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> : factory.Create(p)))
            .ToArray();
    }
}</span></pre>
</div>
<p>总结一下,就是在 DbContext 的派生类中查找符合以下条件的属性:</p>
<p><span style="color: rgba(0, 0, 128, 1)">1、非静态属性;</span></p>
<p><span style="color: rgba(0, 0, 128, 1)">2、不能是索引器;</span></p>
<p><span style="color: rgba(0, 0, 128, 1)">3、属性是 DbSet&lt;&gt; 类型,并且有泛型参数(即实体类型);</span></p>
<p><span style="color: rgba(0, 0, 128, 1)">4、外加一条,属性具有 set 访问器(这个条件是在 InitializeSets 方法的代码中,Where 方法筛选出来)。</span></p>
<p>&nbsp;</p>
<p>到了这里,本文的主题就有了答案了:</p>
<p><span style="color: rgba(0, 0, 128, 1)">DbContext 构造函数 --&gt;&nbsp;IDbSetInitializer --&gt;&nbsp;IDbSetFinder</span></p>
<p>还差一步,前面咱们说过,DbSet&lt;&gt; 实例是由&nbsp;IDbSetSource 负责创建的,其内部实现类是&nbsp;DbSetSource。</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)"> DbSetSource : IDbSetSource
{
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> MethodInfo GenericCreateSet
      </span>= <span style="color: rgba(0, 0, 255, 1)">typeof</span>(DbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory))!<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ConcurrentDictionary&lt;(Type Type, <span style="color: rgba(0, 0, 255, 1)">string</span>? Name), Func&lt;DbContext, <span style="color: rgba(0, 0, 255, 1)">string</span>?, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt;&gt; _cache = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();

    </span><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)">object</span><span style="color: rgba(0, 0, 0, 1)"> Create(DbContext context, Type type)
      </span>=&gt; CreateCore(context, type, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">, GenericCreateSet);


    </span><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)">object</span> Create(DbContext context, <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> name, Type type)
      </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> CreateCore(context, type, name, GenericCreateSet);

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">object</span> CreateCore(DbContext context, Type type, <span style="color: rgba(0, 0, 255, 1)">string</span>?<span style="color: rgba(0, 0, 0, 1)"> name, MethodInfo createMethod)
      </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> _cache.GetOrAdd(
            (type, name),
            </span><span style="color: rgba(0, 0, 255, 1)">static</span> (t, createMethod) =&gt; (Func&lt;DbContext, <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)">)createMethod
                .MakeGenericMethod(t.Type)
                .Invoke(</span><span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">null</span>)!<span style="color: rgba(0, 0, 0, 1)">,
            createMethod)(context, name);

   
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Func&lt;DbContext, <span style="color: rgba(0, 0, 255, 1)">string</span>?, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt; CreateSetFactory&lt;TEntity&gt;<span style="color: rgba(0, 0, 0, 1)">()
      </span><span style="color: rgba(0, 0, 255, 1)">where</span> TEntity : <span style="color: rgba(0, 0, 255, 1)">class</span>
      =&gt; <strong><em><span style="background-color: rgba(255, 255, 0, 1)">(c, name) =&gt; <span style="color: rgba(0, 0, 255, 1)">new</span> InternalDbSet&lt;TEntity&gt;</span></em></strong><span style="color: rgba(0, 0, 0, 1)"><strong><em><span style="background-color: rgba(255, 255, 0, 1)">(c, name)</span></em></strong>;
}</span></pre>
</div>
<p>所以,默认创建的 DbSet&lt;&gt; 实例其实是&nbsp;InternalDbSet&lt;TEntity&gt; 类型。</p>
<p>所有的组件都是通过&nbsp;EntityFrameworkServicesBuilder 类的相关方法来添加到服务容器中的。</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, 0, 1)"> EntityFrameworkServicesBuilder TryAddCoreServices()
    {
      TryAdd</span>&lt;<strong><span style="background-color: rgba(255, 255, 0, 1)">IDbSetFinder, DbSetFinder</span></strong>&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;<strong><span style="background-color: rgba(255, 255, 0, 1)">IDbSetInitializer, DbSetInitializer</span></strong>&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;<strong><span style="background-color: rgba(255, 255, 0, 1)">IDbSetSource, DbSetSource</span></strong>&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IEntityFinderSource, EntityFinderSource&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IEntityMaterializerSource, EntityMaterializerSource&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IProviderConventionSetBuilder, ProviderConventionSetBuilder&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IConventionSetBuilder, RuntimeConventionSetBuilder&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IModelCustomizer, ModelCustomizer&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IModelCacheKeyFactory, ModelCacheKeyFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ILoggerFactory&gt;(p =&gt; ScopedLoggerFactory.Create(p, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">));
      TryAdd</span>&lt;IModelSource, ModelSource&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IModelRuntimeInitializer, ModelRuntimeInitializer&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IInternalEntityEntrySubscriber, InternalEntityEntrySubscriber&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IEntityEntryGraphIterator, EntityEntryGraphIterator&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IEntityGraphAttacher, EntityGraphAttacher&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IValueGeneratorCache, ValueGeneratorCache&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IKeyPropagator, KeyPropagator&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;INavigationFixer, NavigationFixer&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ILocalViewListener, LocalViewListener&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IStateManager, StateManager&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IConcurrencyDetector, ConcurrencyDetector&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IInternalEntityEntryNotifier, InternalEntityEntryNotifier&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IValueGenerationManager, ValueGenerationManager&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IChangeTrackerFactory, ChangeTrackerFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IChangeDetector, ChangeDetector&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IDbContextServices, DbContextServices&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IDbContextDependencies, DbContextDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IDatabaseFacadeDependencies, DatabaseFacadeDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IValueGeneratorSelector, ValueGeneratorSelector&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IModelValidator, ModelValidator&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IExecutionStrategyFactory, ExecutionStrategyFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd(p </span>=&gt; p.GetRequiredService&lt;IExecutionStrategyFactory&gt;<span style="color: rgba(0, 0, 0, 1)">().Create());
      TryAdd</span>&lt;ICompiledQueryCache, CompiledQueryCache&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IAsyncQueryProvider, EntityQueryProvider&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IQueryCompiler, QueryCompiler&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ICompiledQueryCacheKeyGenerator, CompiledQueryCacheKeyGenerator&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ISingletonOptionsInitializer, SingletonOptionsInitializer&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd(</span><span style="color: rgba(0, 0, 255, 1)">typeof</span>(IDiagnosticsLogger&lt;&gt;), <span style="color: rgba(0, 0, 255, 1)">typeof</span>(DiagnosticsLogger&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">));
      TryAdd</span>&lt;IInterceptors, Interceptors&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IInterceptorAggregator, SaveChangesInterceptorAggregator&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IInterceptorAggregator, IdentityResolutionInterceptorAggregator&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IInterceptorAggregator, QueryExpressionInterceptorAggregator&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ILoggingOptions, LoggingOptions&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ICoreSingletonOptions, CoreSingletonOptions&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ISingletonOptions, ILoggingOptions&gt;(p =&gt; p.GetRequiredService&lt;ILoggingOptions&gt;<span style="color: rgba(0, 0, 0, 1)">());
      TryAdd</span>&lt;ISingletonOptions, ICoreSingletonOptions&gt;(p =&gt; p.GetRequiredService&lt;ICoreSingletonOptions&gt;<span style="color: rgba(0, 0, 0, 1)">());
      TryAdd(p </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> GetContextServices(p).Model);
      TryAdd</span>&lt;IDesignTimeModel&gt;(p =&gt; <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DesignTimeModel(GetContextServices(p)));
      TryAdd(p </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> GetContextServices(p).CurrentContext);
      TryAdd</span>&lt;IDbContextOptions&gt;(p =&gt;<span style="color: rgba(0, 0, 0, 1)"> GetContextServices(p).ContextOptions);
      TryAdd</span>&lt;IResettableService, ILazyLoaderFactory&gt;(p =&gt; p.GetRequiredService&lt;ILazyLoaderFactory&gt;<span style="color: rgba(0, 0, 0, 1)">());
      TryAdd</span>&lt;IResettableService, IStateManager&gt;(p =&gt; p.GetRequiredService&lt;IStateManager&gt;<span style="color: rgba(0, 0, 0, 1)">());
      TryAdd</span>&lt;IResettableService, IDbContextTransactionManager&gt;(p =&gt; p.GetRequiredService&lt;IDbContextTransactionManager&gt;<span style="color: rgba(0, 0, 0, 1)">());
      TryAdd</span>&lt;IEvaluatableExpressionFilter, EvaluatableExpressionFilter&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IValueConverterSelector, ValueConverterSelector&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IConstructorBindingFactory, ConstructorBindingFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ILazyLoaderFactory, LazyLoaderFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ILazyLoader&gt;(p =&gt; p.GetRequiredService&lt;ILazyLoaderFactory&gt;<span style="color: rgba(0, 0, 0, 1)">().Create());
      TryAdd</span>&lt;IParameterBindingFactories, ParameterBindingFactories&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IMemberClassifier, MemberClassifier&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IPropertyParameterBindingFactory, PropertyParameterBindingFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IParameterBindingFactory, LazyLoaderParameterBindingFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IParameterBindingFactory, ContextParameterBindingFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IParameterBindingFactory, EntityTypeParameterBindingFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IMemoryCache&gt;(_ =&gt; <span style="color: rgba(0, 0, 255, 1)">new</span> MemoryCache(<span style="color: rgba(0, 0, 255, 1)">new</span> MemoryCacheOptions { SizeLimit = <span style="color: rgba(128, 0, 128, 1)">10240</span><span style="color: rgba(0, 0, 0, 1)"> }));
      TryAdd</span>&lt;IUpdateAdapterFactory, UpdateAdapterFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IQueryCompilationContextFactory, QueryCompilationContextFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IQueryTranslationPreprocessorFactory, QueryTranslationPreprocessorFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IQueryTranslationPostprocessorFactory, QueryTranslationPostprocessorFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;INavigationExpansionExtensibilityHelper, NavigationExpansionExtensibilityHelper&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IExceptionDetector, ExceptionDetector&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IAdHocMapper, AdHocMapper&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;IJsonValueReaderWriterSource, JsonValueReaderWriterSource&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ILiftableConstantFactory, LiftableConstantFactory&gt;<span style="color: rgba(0, 0, 0, 1)">();
      TryAdd</span>&lt;ILiftableConstantProcessor, LiftableConstantProcessor&gt;<span style="color: rgba(0, 0, 0, 1)">();

      TryAdd(
            p </span>=&gt; p.GetService&lt;IDbContextOptions&gt;()?.FindExtension&lt;CoreOptionsExtension&gt;()?<span style="color: rgba(0, 0, 0, 1)">.DbContextLogger
                </span>?? <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> NullDbContextLogger());

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This has to be lazy to avoid creating instances that are not disposed</span>
<span style="color: rgba(0, 0, 0, 1)">      ServiceCollectionMap
            .TryAddSingleton</span>&lt;DiagnosticSource&gt;(_ =&gt; <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DiagnosticListener(DbLoggerCategory.Name));

      ServiceCollectionMap.GetInfrastructure()
            .AddDependencySingleton</span>&lt;LazyLoaderParameterBindingFactoryDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;DatabaseProviderDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;ModelSourceDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;ValueGeneratorCacheDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;ModelValidatorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;TypeMappingSourceDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;ModelCustomizerDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;ModelCacheKeyFactoryDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;ValueConverterSelectorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;EntityMaterializerSourceDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;EvaluatableExpressionFilterDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;RuntimeModelDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;ModelRuntimeInitializerDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;NavigationExpansionExtensibilityHelperDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;JsonValueReaderWriterSourceDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencySingleton</span>&lt;LiftableConstantExpressionDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;ProviderConventionSetBuilderDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;QueryCompilationContextDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;StateManagerDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;ExecutionStrategyDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;CompiledQueryCacheKeyGeneratorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;QueryContextDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;QueryableMethodTranslatingExpressionVisitorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;QueryTranslationPreprocessorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;QueryTranslationPostprocessorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;ShapedQueryCompilingExpressionVisitorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;ValueGeneratorSelectorDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;DatabaseDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;ModelDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;ModelCreationDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">()
            .AddDependencyScoped</span>&lt;AdHocMapperDependencies&gt;<span style="color: rgba(0, 0, 0, 1)">();

      ServiceCollectionMap.TryAddSingleton</span>&lt;IRegisteredServices&gt;<span style="color: rgba(0, 0, 0, 1)">(
            </span><span style="color: rgba(0, 0, 255, 1)">new</span> RegisteredServices(ServiceCollectionMap.ServiceCollection.Select(s =&gt;<span style="color: rgba(0, 0, 0, 1)"> s.ServiceType)));

      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
    }</span></pre>
</div>
<p>&nbsp;</p>
<p>DbContext 对象在初始化时只是查找实体集合,此时还没有任何查询被执行。当咱们要访问实体数据时,DbSet&lt;&gt; 会把查询任务交给&nbsp;IAsyncQueryProvider 接口的实现类去处理,它的内部实现类是&nbsp;EntityQueryProvider。</p>
<p>EntityQueryProvider 内部基于 LINQ 生成表达式树,表达式树传递给&nbsp;IQueryCompiler 去编译并运行。IQueryCompiler 接口有个内部实现类叫&nbsp;QueryCompiler。</p>
<p>后面就一路往下传递到数据库层,执行生成的SQL。当然这里头还包含很多复杂的组件,此处咱们就不继续挖,否则要挖到明天早上。</p>
<p>&nbsp;</p>
<p>本文老周只讲述了和 DbContext 类添加实体集合相关的组件,其他组件等后面说到相关内容再介绍。咱们总不能一口气把整个框架都说一遍的,太复杂了。</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/tcjiaan/p/18956032
頁: [1]
查看完整版本: 【EF Core】DbContext是如何识别出实体集合的