在使用外部模型那篇水文中,有大伙伴提出:老周,你那个 Ultraman 类和 Speciality 类的的关系是不是有问题,外键不应该在 Speciality 类上吗,怎么会跑到 Ultraman 类上?因为它们是一对一关系,在配置的时候你也可以反过来,主要区别是谁引用谁的问题,由于是一对一引用,所以反过来也可以的。
今天咱们聊聊实体类构造函数的依赖注入。实体类也支持依赖注入,不过,目前版本只支持注入 EF Core 自己的服务类型,你在代码中添加的应用程序级服务类型不能注入(以后可能会支持)。总结一下,以下服务类型可以注入:
现在要解决一个问题:我怎么知道 EF Core 框架内部哪些服务可以注入?这个活儿有两个方案:第一个方案是看 EF Core 源代码,在 EFCore\Infrastructure 下,注意看 EntityFrameworkServicesBuilder 类的 CoreServices 字段或 TryAddCoreServices 方法,基本齐全了。
public static readonly IDictionary<Type, ServiceCharacteristics> CoreServices
= new Dictionary<Type, ServiceCharacteristics>
{
{ typeof(LoggingDefinitions), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IDatabaseProvider), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(IDbSetFinder), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IDbSetInitializer), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IDbSetSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IEntityFinderSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IStructuralTypeMaterializerSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ITypeMappingSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModelCustomizer), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModelCacheKeyFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModelSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModelRuntimeInitializer), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IInternalEntrySubscriber), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IEntityEntryGraphIterator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IValueGeneratorCache), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ISingletonOptionsInitializer), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ILoggingOptions), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ICoreSingletonOptions), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModelValidator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ICompiledQueryCache), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IValueConverterSelector), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IConstructorBindingFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRegisteredServices), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IPropertyParameterBindingFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IParameterBindingFactories), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMemberClassifier), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMemoryCache), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IEvaluatableExpressionFilter), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(INavigationExpansionExtensibilityHelper), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ILiftableConstantFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IExceptionDetector), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IJsonValueReaderWriterSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IProviderConventionSetBuilder), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IConventionSetBuilder), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDiagnosticsLogger<>), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IInterceptors), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ILoggerFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IEntityGraphAttacher), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IKeyPropagator), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(INavigationFixer), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ILocalViewListener), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IStateManager), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IConcurrencyDetector), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IInternalEntityEntryNotifier), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IValueGenerationManager), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IChangeTrackerFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IChangeDetector), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDbContextServices), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IValueGeneratorSelector), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IExecutionStrategyFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IExecutionStrategy), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IAsyncQueryProvider), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IQueryCompiler), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ICompiledQueryCacheKeyGenerator), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IModel), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDesignTimeModel), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IUpdateAdapterFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ICurrentDbContext), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDbContextDependencies), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDatabaseFacadeDependencies), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDbContextOptions), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDatabase), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDatabaseCreator), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDbContextTransactionManager), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IQueryContextFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IQueryCompilationContextFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IQueryableMethodTranslatingExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IQueryTranslationPreprocessorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IQueryTranslationPostprocessorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IShapedQueryCompilingExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDbContextLogger), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IAdHocMapper), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ILiftableConstantProcessor), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(ILazyLoader), new ServiceCharacteristics(ServiceLifetime.Transient) },
{ typeof(ILazyLoaderFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IParameterBindingFactory), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(ITypeMappingSourcePlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{
typeof(IEvaluatableExpressionFilterPlugin),
new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true)
},
{ typeof(ISingletonOptions), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(IConventionSetPlugin), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
{ typeof(ISingletonInterceptor), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(IResettableService), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
{ typeof(IInterceptor), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
{ typeof(IInterceptorAggregator), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) }
};
然后解决第二个问题:在实体类的构造函数中注入服务有啥用?大多数时候没啥用,但当你要使用某些服务来获取特殊信息,或执行特殊操作(如在实体类中执行 SQL 语句)时就用得上。
咱们举个例子,就用官方文档最喜欢的 Blog 和 Post 实体来演示。其中,Blog 类的构造函数将注入 IEntityType 服务。然后可以利用这个服务来获取 Blog 实体有几个属性,有几个键,有几个导航属性。
注意,你自己 new 一个 Blog 实例是没有注入功能的,所以要定义一个无参数的公共构造函数,以供外部初始化用。另一个私有构造函数有一个 IEntityType 类型的参数,可以接收注入。因为这构造函数只由 EF Core 内部调用,代码中调用无法注入,故声明为私有即可。EF Core 在查找构造函数时,不管你是公有的还是私有的,只要不是静态的,一律能发现。
上面示例中,MyDBContext 实例会注入到每个 Screen 实例的 ThisContext 属性。即每个实体实例的 ThisContext 属性都引用同一个 MyDBContext 对象。