最近在接触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点了,就写到这里后面我会跟上后续的内容,谢谢你们的阅读,若是有任何不理解或者指正的地方欢迎评论,最后谢谢你们。