EF Core 源码分析

最近在接触DDD+micro service来开发项目,由于EF Core太适合DDD模式须要的ORM设计,因此这篇博客是从代码角度去理解EF core的内部实现,但愿你们能从其中学到一些心得体会去更好的写出高质量的代码。git

 从github 上去下载ef core仓库, 本篇代码的版本是基于tag v5.0.3的,若是你们在之后看见这篇博客,能够在分支上reset 到这个tag对照这边博客,下载完成以后,配置根目录下的global.json成本机已经安装的sdk, runtime 的版本,直接build经过就能够了。下面是代码的目录。github

 

 

 简单的说一下,benchmark 是用来性能测试的,solution是一些自动化部署的配置文件,src是ef core 的核心代码,test 是此项目的单元测试目录,若是你想更深刻了解每一个模块的实现逻辑,从单元测试出发是一个很是不错的选择。数据库

在用户阶段咱们须要作的以下,代码来自 ef 官方文档json

 1 using System;
 2 using System.Linq;
 3 
 4 namespace EFGetStarted
 5 {
 6     internal class Program
 7     {
 8         private static void Main()
 9         {
10             using (var db = new BloggingContext())
11             {
12                 // Note: This sample requires the database to be created before running.
13 
14                 // Create
15                 Console.WriteLine("Inserting a new blog");
16                 db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
17                 db.SaveChanges();
18 
19                 // Read
20                 Console.WriteLine("Querying for a blog");
21                 var blog = db.Blogs
22                     .OrderBy(b => b.BlogId)
23                     .First();
24 
25                 // Update
26                 Console.WriteLine("Updating the blog and adding a post");
27                 blog.Url = "https://devblogs.microsoft.com/dotnet";
28                 blog.Posts.Add(
29                     new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
30                 db.SaveChanges();
31 
32                 // Delete
33                 Console.WriteLine("Delete the blog");
34                 db.Remove(blog);
35                 db.SaveChanges();
36             }
37         }
38     }
39 }

首先咱们先看在new DbContext的时候作了哪些的初始化的动做数据结构

1         protected DbContext()
2             : this(new DbContextOptions<DbContext>())
3         {
4         }

这个Context建立了了本身的DbContextOptions,很明显咱们须要的一些配置像数据库组件等信息都是须要在DbContextOptions里配置的,咱们继续看下去会发如今初始化的时候会建立一个字典对象,正如其名所说的同样这是在维护一些扩展组件。当你想使用不一样类型的数据库是引用的library 就是在动态注册这些组件。在本例中,咱们使用InMemoryDatabse来测试。  app

1      protected DbContextOptions(
2             [NotNull] IReadOnlyDictionary<Type, IDbContextOptionsExtension> extensions)
3         {
4             Check.NotNull(extensions, nameof(extensions));
5 
6             _extensions = extensions;
7         }

 在相应的DbContext的继承子类重写OnConfiguring方法就可使用memory database,配置以下less

1         protected internal override void OnConfiguring(DbContextOptionsBuilder options)
2         {
3             options.UseInMemoryDatabase(nameof(BloggingContext));
4         }

初始化新的option好的时候,这是一个空的配置文件,后面就是对option作的初始化的动做。ide

 1   public DbContext([NotNull] DbContextOptions options)
 2         {
 3             Check.NotNull(options, nameof(options));
 4 
 5             if (!options.ContextType.IsAssignableFrom(GetType()))
 6             {
 7                 throw new InvalidOperationException(CoreStrings.NonGenericOptions(GetType().ShortDisplayName()));
 8             }
 9 
10             _options = options;
11 
12             // This service is not stored in _setInitializer as this may not be the service provider that will be used
13             // as the internal service provider going forward, because at this time OnConfiguring has not yet been called.
14             // Mostly that isn't a problem because set initialization is done by our internal services, but in the case
15             // where some of those services are replaced, this could initialize set using non-replaced services.
16             // In this rare case if this is a problem for the app, then the app can just not use this mechanism to create
17             // DbSet instances, and this code becomes a no-op. However, if this set initializer is then saved and used later
18             // for the Set method, then it makes the problem bigger because now an app is using the non-replaced services
19             // even when it doesn't need to.
20             ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: false)
21                 .GetRequiredService<IDbSetInitializer>()
22                 .InitializeSets(this);
23 
24             EntityFrameworkEventSource.Log.DbContextInitializing();
25         }

check完以后,ServiceProviderCache的单例模式去获取内部的service provide,这个很明显是对须要的组件进行依赖注入。咱们截取了一些核心代码以下。post

 1         public virtual IServiceProvider GetOrAdd([NotNull] IDbContextOptions options, bool providerRequired)
 2         {
 3             var key = options.Extensions
 4                 .OrderBy(e => e.GetType().Name)
 5                 .Aggregate(0L, (t, e) => (t * 397) ^ ((long)e.GetType().GetHashCode() * 397) ^ e.Info.GetServiceProviderHashCode());
 6 
 7             return _configurations.GetOrAdd(key, k => BuildServiceProvider()).ServiceProvider;
 8 
 9             (IServiceProvider ServiceProvider, IDictionary<string, string> DebugInfo) BuildServiceProvider()
10             {
11 
12                 var services = new ServiceCollection();
13                 var hasProvider = ApplyServices(options, services);
14 
15                 var serviceProvider = services.BuildServiceProvider();
16 
17                 if (hasProvider)
18                 {
19                     serviceProvider
20                         .GetRequiredService<ISingletonOptionsInitializer>()
21                         .EnsureInitialized(serviceProvider, options);
22                 }
23 
24                 return (serviceProvider, debugInfo);
25             }
26         }

从上面能够看出,option 的注册扩展组件不同会建立不同的service provide,在第13行代码会进行依赖注入,咱们点进去以后会看到以下代码。性能

 1         private static bool ApplyServices(IDbContextOptions options, ServiceCollection services)
 2         {
 3             var coreServicesAdded = false;
 4 
 5             foreach (var extension in options.Extensions)
 6             {
 7                 extension.ApplyServices(services);
 8 
 9                 if (extension.Info.IsDatabaseProvider)
10                 {
11                     coreServicesAdded = true;
12                 }
13             }
14 
15             if (coreServicesAdded)
16             {
17                 return true;
18             }
19 
20             new EntityFrameworkServicesBuilder(services).TryAddCoreServices();
21 
22             return false;
23         }

循环option获取其中的扩展组件,每一个组件会有本身的依赖对象注入,若是没找到database provider,再注入当前的context的核心依赖对象注入,注入的逻辑不用看了,后面去查接口的实现对象参考这个就好了。代码到如今咱们能看到已经获取service provider了,接下来继续看InitializeSets的方法。

1    public virtual void InitializeSets(DbContext context)
2         {
3             foreach (var setInfo in _setFinder.FindSets(context.GetType()).Where(p => p.Setter != null))
4             {
5                 setInfo.Setter.SetClrValue(
6                     context,
7                     ((IDbSetCache)context).GetOrAddSet(_setSource, setInfo.Type));
8             }
9         }

咱们记得以前声明dbcontext 的时候咱们会写dbset 属性,可是并无对其进行赋值,而且全部对表的操做仿佛都是经过这个对象来完成的,是由于dbcontext 帮咱们自动作了赋值的操做,咱们找到findSets的实现逻辑。

 1  public virtual IReadOnlyList<DbSetProperty> FindSets(Type contextType)
 2             => _cache.GetOrAdd(contextType, FindSetsNonCached);
 3 
 4         private static DbSetProperty[] FindSetsNonCached(Type contextType)
 5         {
 6             var factory = new ClrPropertySetterFactory();
 7 
 8             return contextType.GetRuntimeProperties()
 9                 .Where(
10                     p => !p.IsStatic()
11                         && !p.GetIndexParameters().Any()
12                         && p.DeclaringType != typeof(DbContext)
13                         && p.PropertyType.GetTypeInfo().IsGenericType
14                         && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
15                 .OrderBy(p => p.Name)
16                 .Select(
17                     p => new DbSetProperty(
18                         p.Name,
19                         p.PropertyType.GenericTypeArguments.Single(),
20                         p.SetMethod == null ? null : factory.Create(p)))
21                 .ToArray();
22         }

上面的代码会显示不是静态的,不是索引,type不是dbcontext的,是泛型的,是dbset泛型的属性会当作dbcontext 的set来处理,而后map成DbSetProperty对象进行维护,值得注意的一点是SetMethod为factory.Create(p)返回的Func类型,这个时候对象并无赋值,指到在上面代码setInfo.Setter.SetClrValue调用完这个set才是真正的进行赋值,而其中的逻辑是以下所示。

 

1      [UsedImplicitly]
2         private static Func<DbContext, string, object> CreateSetFactory<TEntity>()
3             where TEntity : class
4             => (c, name) => new InternalDbSet<TEntity>(c, name);

其中的dbset 属性就是一个个的InternalDbSet对象,初始化set以后dbcontext就是日志记录一下,这个不是个人研究重点对象,到此为止一个dbcontext对象建立成功,这个时候咱们的疑问就来了,咱们的数据库配置的组件等配置是何时初始化的呢,明显如今的option 是很是干净的。

不用急咱们如今来看一下第一个代码片断中的db.Add方法吧,看一下这个里面作了啥。

 1         private EntityEntry<TEntity> SetEntityState<TEntity>(
 2             TEntity entity,
 3             EntityState entityState)
 4             where TEntity : class
 5         {
 6             var entry = EntryWithoutDetectChanges(entity);
 7 
 8             SetEntityState(entry.GetInfrastructure(), entityState);
 9 
10             return entry;
11         }

咱们会传入一个entity对象,而且将entityState 置为add 的状态,这个时候咱们会涉及到一个重要的对象,就是DbContextDependencies.StateManager,这个就是内部的entity的状态管理对象,咱们稍后会讨论这个对象。

1    private EntityEntry<TEntity> EntryWithoutDetectChanges<TEntity>(TEntity entity)
2             where TEntity : class
3             => new(DbContextDependencies.StateManager.GetOrCreateEntry(entity));

这个时候咱们须要重视DbContextDependencies这个对象,也就是dbcontext的依赖对象,它会经过InternalServiceProvider对象来得到,可是其中会有一些初始化的逻辑

 1        private IServiceProvider InternalServiceProvider
 2         {
 3             get
 4             {
 5                 CheckDisposed();
 6 
 7                 if (_contextServices != null)
 8                 {
 9                     return _contextServices.InternalServiceProvider;
10                 }
11 
12                 if (_initializing)
13                 {
14                     throw new InvalidOperationException(CoreStrings.RecursiveOnConfiguring);
15                 }
16 
17                 try
18                 {
19                     _initializing = true;
20 
21                     var optionsBuilder = new DbContextOptionsBuilder(_options);
22 
23                     OnConfiguring(optionsBuilder);
24 
25                     if (_options.IsFrozen
26                         && !ReferenceEquals(_options, optionsBuilder.Options))
27                     {
28                         throw new InvalidOperationException(CoreStrings.PoolingOptionsModified);
29                     }
30 
31                     var options = optionsBuilder.Options;
32 
33                     _serviceScope = ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: true)
34                         .GetRequiredService<IServiceScopeFactory>()
35                         .CreateScope();
36 
37                     var scopedServiceProvider = _serviceScope.ServiceProvider;
38 
39                     var contextServices = scopedServiceProvider.GetService<IDbContextServices>();
40 
41                     contextServices.Initialize(scopedServiceProvider, options, this);
42 
43                     _contextServices = contextServices;
44 
45                     DbContextDependencies.InfrastructureLogger.ContextInitialized(this, options);
46                 }
47                 finally
48                 {
49                     _initializing = false;
50                 }
51 
52                 return _contextServices.InternalServiceProvider;
53             }
54         }

在23行的OnConfiguring方法就是会调用咱们配置的数据库组件,本例中咱们用的就是inmemorydatabase,咱们如今撇一下这个组件中间作了啥。很简单的就是在option中注册了扩展组件InMemoryOptionsExtension,以前说过注册了组件以后会从新生成新的server provider, 在新的server collection 从新注入memory database 组件所须要的依赖对象。在扩展组件的InMemoryOptionsExtension.ApplyServices 方法。这是每一个扩展组件必需要实现的方法。如今咱们知道database组件如今已经注册进来了。继续查看contetx.add 方法的逻辑。

 1      private IServiceProvider InternalServiceProvider
 2         {
 3             get
 4             {
 5                 CheckDisposed();
 6 
 7                 if (_contextServices != null)
 8                 {
 9                     return _contextServices.InternalServiceProvider;
10                 }
11 
12                 if (_initializing)
13                 {
14                     throw new InvalidOperationException(CoreStrings.RecursiveOnConfiguring);
15                 }
16 
17                 try
18                 {
19                     _initializing = true;
20 
21                     var optionsBuilder = new DbContextOptionsBuilder(_options);
22 
23                     OnConfiguring(optionsBuilder);
24 
25                     if (_options.IsFrozen
26                         && !ReferenceEquals(_options, optionsBuilder.Options))
27                     {
28                         throw new InvalidOperationException(CoreStrings.PoolingOptionsModified);
29                     }
30 
31                     var options = optionsBuilder.Options;
32 
33                     _serviceScope = ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: true)
34                         .GetRequiredService<IServiceScopeFactory>()
35                         .CreateScope();
36 
37                     var scopedServiceProvider = _serviceScope.ServiceProvider;
38 
39                     var contextServices = scopedServiceProvider.GetService<IDbContextServices>();
40 
41                     contextServices.Initialize(scopedServiceProvider, options, this);
42 
43                     _contextServices = contextServices;
44 
45                     DbContextDependencies.InfrastructureLogger.ContextInitialized(this, options);
46                 }
47                 finally
48                 {
49                     _initializing = false;
50                 }
51 
52                 return _contextServices.InternalServiceProvider;
53             }
54         }

 

如今转到上上上个代码片断,statemanager 须要建立一个entity,这时候会判断这个entity存在不存在,若是不存在会在facyor 方法建立一个statemanager管理的entity,而后在statemanager更新这个entity的状态。

 1     public virtual InternalEntityEntry GetOrCreateEntry(object entity)
 2         {
 3             var entry = TryGetEntry(entity);
 4             if (entry == null)
 5             {
 6                 var entityType = _model.FindRuntimeEntityType(entity.GetType());
 7                 if (entityType == null)
 8                 {
 9                     if (_model.IsShared(entity.GetType()))
10                     {
11                         throw new InvalidOperationException(
12                             CoreStrings.UntrackedDependentEntity(
13                                 entity.GetType().ShortDisplayName(),
14                                 "." + nameof(EntityEntry.Reference) + "()." + nameof(ReferenceEntry.TargetEntry),
15                                 "." + nameof(EntityEntry.Collection) + "()." + nameof(CollectionEntry.FindEntry) + "()"));
16                     }
17 
18                     throw new InvalidOperationException(CoreStrings.EntityTypeNotFound(entity.GetType().ShortDisplayName()));
19                 }
20 
21                 if (entityType.FindPrimaryKey() == null)
22                 {
23                     throw new InvalidOperationException(CoreStrings.KeylessTypeTracked(entityType.DisplayName()));
24                 }
25 
26                 entry = _internalEntityEntryFactory.Create(this, entityType, entity);
27 
28                 UpdateReferenceMaps(entry, EntityState.Detached, null);
29             }
30 
31             return entry;
32         }

statemanager会有五个对象分别记录每个entity的不一样的记录,以下图所示

 1      switch (state)
 2                     {
 3                         case EntityState.Detached:
 4                             _detachedReferenceMap ??= new Dictionary<object, InternalEntityEntry>(LegacyReferenceEqualityComparer.Instance);
 5                             _detachedReferenceMap[mapKey] = entry;
 6                             break;
 7                         case EntityState.Unchanged:
 8                             _unchangedReferenceMap ??=
 9                                 new Dictionary<object, InternalEntityEntry>(LegacyReferenceEqualityComparer.Instance);
10                             _unchangedReferenceMap[mapKey] = entry;
11                             break;
12                         case EntityState.Deleted:
13                             _deletedReferenceMap ??= new Dictionary<object, InternalEntityEntry>(LegacyReferenceEqualityComparer.Instance);
14                             _deletedReferenceMap[mapKey] = entry;
15                             break;
16                         case EntityState.Modified:
17                             _modifiedReferenceMap ??= new Dictionary<object, InternalEntityEntry>(LegacyReferenceEqualityComparer.Instance);
18                             _modifiedReferenceMap[mapKey] = entry;
19                             break;
20                         case EntityState.Added:
21                             _addedReferenceMap ??= new Dictionary<object, InternalEntityEntry>(LegacyReferenceEqualityComparer.Instance);
22                             _addedReferenceMap[mapKey] = entry;
23                             break;
24                     }

随后在这个entity会进入到一个changetracking 的状态。

好了今天写到这个地方了,进入tracking 的状态时会有一个graph 对象所管理,其中拥有一些图的数据结构,如今快11点了,就写到这里后面我会跟上后续的内容,谢谢你们的阅读,若是有任何不理解或者指正的地方欢迎评论,最后谢谢你们。

相关文章
相关标签/搜索