【EF Core】DbContext是如何识别出实体集合的
<p>在开始之前说明一下,你不要指望阅读完本文后会得到光,就算得到光你也未必能变成迪迦。本文老周仅介绍原理,可以给部分大伙伴们解惑。</p><p>咱们都知道,在派生 DbContext 类时,集体类的集合用 DbSet<TEntity> 表示,而咱们最常用的方法是在 DbContext 的派生类中公开 DbSet<TEntity> 属性。但在实例化 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<(Type Type, <span style="color: rgba(0, 0, 255, 1)">string</span>? Name), <span style="color: rgba(0, 0, 255, 1)">object</span>>? _sets;</pre>
</div>
<p>这行代码老周严重希望你能看懂,看不懂会很麻烦的哟。这是一个字典类型,没错吧。然后,Key是啥类型,Value是啥类型?</p>
<p>Key:是一个二元元组,第一项为 Type 对象,第二项为字符串对象。type 指的是实体类的 Type,name 指的是你为这个实体集合分配的名字。有伙伴会问,我怎么给它命名,DbSet 实例又不是我创建的?不急,请看下文;</p>
<p>Value:猜得出来,这是与实体集合相关的实例,DbSet<>,实际类型是内部类 InternalDbSet<TEntity>。这个后面咱们再说。</p>
<p>咱们先不去关心 DbSet<TEntity> 实例是怎么创建的(因为这里面要绕绕弯子),至少咱们知道:在DbContext上声明的实体集合是缓存到一个字典中的。而把集合实例添加到字典中的是一个名为 GetOrAddSet 的方法。注意该方法是显示实现了 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><<span style="color: rgba(0, 0, 255, 1)">object</span>><span style="color: rgba(0, 0, 0, 1)"> GetSets();
}</span></pre>
</div>
<p>IDbSetSource 接口的实现者就是跟创建 DbSet 实例有关的,咱们先忽略它。把注意放那两个重载方法 GetOrAddSet 上,它的功能就是获取或者添加实体集合的引用。咱们看到,这两个重载的区别在:1、以Type为标识添加;2、以Type + name为标识添加。而 DbContext 类是显式实现了 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<TEntity> Set< TEntity><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>
=> (DbSet<TEntity>)((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<TEntity> Set< TEntity>(<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>
=> (DbSet<TEntity>)((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> </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<Student> Students => Set<Student><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<Student> Students => Set<Student>(<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<Student> 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>这就要用到另一个辅助—— IDbSetInitializer,其实现类为 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 => 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>这个 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)"><IDbSetInitializer></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<> 这一功能的是名为 IDbSetFinder 的组件,它的内部实现类为 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<Type, IReadOnlyList<DbSetProperty>> _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<DbSetProperty><span style="color: rgba(0, 0, 0, 1)"> FindSets(Type contextType)
</span>=><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)">=> !</span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&& !</span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&& p.DeclaringType != <span style="color: rgba(0, 0, 255, 1)">typeof</span></span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&&</span></strong><strong><span style="background-color: rgba(255, 255, 0, 1)">&& p.PropertyType.GetGenericTypeDefinition() == <span style="color: rgba(0, 0, 255, 1)">typeof</span>(DbSet<></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>=><span style="color: rgba(0, 0, 0, 1)"> p.Name)
.Select(
p </span>=> <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<> 类型,并且有泛型参数(即实体类型);</span></p>
<p><span style="color: rgba(0, 0, 128, 1)">4、外加一条,属性具有 set 访问器(这个条件是在 InitializeSets 方法的代码中,Where 方法筛选出来)。</span></p>
<p> </p>
<p>到了这里,本文的主题就有了答案了:</p>
<p><span style="color: rgba(0, 0, 128, 1)">DbContext 构造函数 --> IDbSetInitializer --> IDbSetFinder</span></p>
<p>还差一步,前面咱们说过,DbSet<> 实例是由 IDbSetSource 负责创建的,其内部实现类是 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<(Type Type, <span style="color: rgba(0, 0, 255, 1)">string</span>? Name), Func<DbContext, <span style="color: rgba(0, 0, 255, 1)">string</span>?, <span style="color: rgba(0, 0, 255, 1)">object</span>>> _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>=> 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>=><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>=><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) => (Func<DbContext, <span style="color: rgba(0, 0, 255, 1)">string</span>?, <span style="color: rgba(0, 0, 255, 1)">object</span>><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<DbContext, <span style="color: rgba(0, 0, 255, 1)">string</span>?, <span style="color: rgba(0, 0, 255, 1)">object</span>> CreateSetFactory<TEntity><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>
=> <strong><em><span style="background-color: rgba(255, 255, 0, 1)">(c, name) => <span style="color: rgba(0, 0, 255, 1)">new</span> InternalDbSet<TEntity></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<> 实例其实是 InternalDbSet<TEntity> 类型。</p>
<p>所有的组件都是通过 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><<strong><span style="background-color: rgba(255, 255, 0, 1)">IDbSetFinder, DbSetFinder</span></strong>><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><<strong><span style="background-color: rgba(255, 255, 0, 1)">IDbSetInitializer, DbSetInitializer</span></strong>><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><<strong><span style="background-color: rgba(255, 255, 0, 1)">IDbSetSource, DbSetSource</span></strong>><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IEntityFinderSource, EntityFinderSource><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IEntityMaterializerSource, EntityMaterializerSource><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IProviderConventionSetBuilder, ProviderConventionSetBuilder><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IConventionSetBuilder, RuntimeConventionSetBuilder><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IModelCustomizer, ModelCustomizer><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IModelCacheKeyFactory, ModelCacheKeyFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ILoggerFactory>(p => ScopedLoggerFactory.Create(p, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">));
TryAdd</span><IModelSource, ModelSource><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IModelRuntimeInitializer, ModelRuntimeInitializer><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IInternalEntityEntrySubscriber, InternalEntityEntrySubscriber><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IEntityEntryGraphIterator, EntityEntryGraphIterator><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IEntityGraphAttacher, EntityGraphAttacher><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IValueGeneratorCache, ValueGeneratorCache><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IKeyPropagator, KeyPropagator><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><INavigationFixer, NavigationFixer><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ILocalViewListener, LocalViewListener><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IStateManager, StateManager><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IConcurrencyDetector, ConcurrencyDetector><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IInternalEntityEntryNotifier, InternalEntityEntryNotifier><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IValueGenerationManager, ValueGenerationManager><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IChangeTrackerFactory, ChangeTrackerFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IChangeDetector, ChangeDetector><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IDbContextServices, DbContextServices><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IDbContextDependencies, DbContextDependencies><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IDatabaseFacadeDependencies, DatabaseFacadeDependencies><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IValueGeneratorSelector, ValueGeneratorSelector><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IModelValidator, ModelValidator><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IExecutionStrategyFactory, ExecutionStrategyFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd(p </span>=> p.GetRequiredService<IExecutionStrategyFactory><span style="color: rgba(0, 0, 0, 1)">().Create());
TryAdd</span><ICompiledQueryCache, CompiledQueryCache><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IAsyncQueryProvider, EntityQueryProvider><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IQueryCompiler, QueryCompiler><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ICompiledQueryCacheKeyGenerator, CompiledQueryCacheKeyGenerator><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ISingletonOptionsInitializer, SingletonOptionsInitializer><span style="color: rgba(0, 0, 0, 1)">();
TryAdd(</span><span style="color: rgba(0, 0, 255, 1)">typeof</span>(IDiagnosticsLogger<>), <span style="color: rgba(0, 0, 255, 1)">typeof</span>(DiagnosticsLogger<><span style="color: rgba(0, 0, 0, 1)">));
TryAdd</span><IInterceptors, Interceptors><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IInterceptorAggregator, SaveChangesInterceptorAggregator><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IInterceptorAggregator, IdentityResolutionInterceptorAggregator><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IInterceptorAggregator, QueryExpressionInterceptorAggregator><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ILoggingOptions, LoggingOptions><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ICoreSingletonOptions, CoreSingletonOptions><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ISingletonOptions, ILoggingOptions>(p => p.GetRequiredService<ILoggingOptions><span style="color: rgba(0, 0, 0, 1)">());
TryAdd</span><ISingletonOptions, ICoreSingletonOptions>(p => p.GetRequiredService<ICoreSingletonOptions><span style="color: rgba(0, 0, 0, 1)">());
TryAdd(p </span>=><span style="color: rgba(0, 0, 0, 1)"> GetContextServices(p).Model);
TryAdd</span><IDesignTimeModel>(p => <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DesignTimeModel(GetContextServices(p)));
TryAdd(p </span>=><span style="color: rgba(0, 0, 0, 1)"> GetContextServices(p).CurrentContext);
TryAdd</span><IDbContextOptions>(p =><span style="color: rgba(0, 0, 0, 1)"> GetContextServices(p).ContextOptions);
TryAdd</span><IResettableService, ILazyLoaderFactory>(p => p.GetRequiredService<ILazyLoaderFactory><span style="color: rgba(0, 0, 0, 1)">());
TryAdd</span><IResettableService, IStateManager>(p => p.GetRequiredService<IStateManager><span style="color: rgba(0, 0, 0, 1)">());
TryAdd</span><IResettableService, IDbContextTransactionManager>(p => p.GetRequiredService<IDbContextTransactionManager><span style="color: rgba(0, 0, 0, 1)">());
TryAdd</span><IEvaluatableExpressionFilter, EvaluatableExpressionFilter><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IValueConverterSelector, ValueConverterSelector><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IConstructorBindingFactory, ConstructorBindingFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ILazyLoaderFactory, LazyLoaderFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ILazyLoader>(p => p.GetRequiredService<ILazyLoaderFactory><span style="color: rgba(0, 0, 0, 1)">().Create());
TryAdd</span><IParameterBindingFactories, ParameterBindingFactories><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IMemberClassifier, MemberClassifier><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IPropertyParameterBindingFactory, PropertyParameterBindingFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IParameterBindingFactory, LazyLoaderParameterBindingFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IParameterBindingFactory, ContextParameterBindingFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IParameterBindingFactory, EntityTypeParameterBindingFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IMemoryCache>(_ => <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><IUpdateAdapterFactory, UpdateAdapterFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IQueryCompilationContextFactory, QueryCompilationContextFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IQueryTranslationPreprocessorFactory, QueryTranslationPreprocessorFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IQueryTranslationPostprocessorFactory, QueryTranslationPostprocessorFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><INavigationExpansionExtensibilityHelper, NavigationExpansionExtensibilityHelper><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IExceptionDetector, ExceptionDetector><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IAdHocMapper, AdHocMapper><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><IJsonValueReaderWriterSource, JsonValueReaderWriterSource><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ILiftableConstantFactory, LiftableConstantFactory><span style="color: rgba(0, 0, 0, 1)">();
TryAdd</span><ILiftableConstantProcessor, LiftableConstantProcessor><span style="color: rgba(0, 0, 0, 1)">();
TryAdd(
p </span>=> p.GetService<IDbContextOptions>()?.FindExtension<CoreOptionsExtension>()?<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><DiagnosticSource>(_ => <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><LazyLoaderParameterBindingFactoryDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><DatabaseProviderDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><ModelSourceDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><ValueGeneratorCacheDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><ModelValidatorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><TypeMappingSourceDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><ModelCustomizerDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><ModelCacheKeyFactoryDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><ValueConverterSelectorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><EntityMaterializerSourceDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><EvaluatableExpressionFilterDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><RuntimeModelDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><ModelRuntimeInitializerDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><NavigationExpansionExtensibilityHelperDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><JsonValueReaderWriterSourceDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencySingleton</span><LiftableConstantExpressionDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><ProviderConventionSetBuilderDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><QueryCompilationContextDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><StateManagerDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><ExecutionStrategyDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><CompiledQueryCacheKeyGeneratorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><QueryContextDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><QueryableMethodTranslatingExpressionVisitorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><QueryTranslationPreprocessorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><QueryTranslationPostprocessorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><ShapedQueryCompilingExpressionVisitorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><ValueGeneratorSelectorDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><DatabaseDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><ModelDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><ModelCreationDependencies><span style="color: rgba(0, 0, 0, 1)">()
.AddDependencyScoped</span><AdHocMapperDependencies><span style="color: rgba(0, 0, 0, 1)">();
ServiceCollectionMap.TryAddSingleton</span><IRegisteredServices><span style="color: rgba(0, 0, 0, 1)">(
</span><span style="color: rgba(0, 0, 255, 1)">new</span> RegisteredServices(ServiceCollectionMap.ServiceCollection.Select(s =><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> </p>
<p>DbContext 对象在初始化时只是查找实体集合,此时还没有任何查询被执行。当咱们要访问实体数据时,DbSet<> 会把查询任务交给 IAsyncQueryProvider 接口的实现类去处理,它的内部实现类是 EntityQueryProvider。</p>
<p>EntityQueryProvider 内部基于 LINQ 生成表达式树,表达式树传递给 IQueryCompiler 去编译并运行。IQueryCompiler 接口有个内部实现类叫 QueryCompiler。</p>
<p>后面就一路往下传递到数据库层,执行生成的SQL。当然这里头还包含很多复杂的组件,此处咱们就不继续挖,否则要挖到明天早上。</p>
<p> </p>
<p>本文老周只讲述了和 DbContext 类添加实体集合相关的组件,其他组件等后面说到相关内容再介绍。咱们总不能一口气把整个框架都说一遍的,太复杂了。</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/tcjiaan/p/18956032
頁:
[1]