经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
【源码解读(一)】EFCORE源码解读之创建DBContext查询拦截
来源:cnblogs  作者:四处观察  时间:2023/10/16 16:07:55  对本文有异议

引言

    在网上很少看到有关于系统讲解EFCore源码的,可能大概也许是因为EFCore的源码总体是没有asp.net web的源码流程清晰,正如群友所说,EFCore的源码大致看起来有点凌乱,与其说凌乱,不如说是没有一个好的方向;然后昨天在群里有一个朋友再说,EfCore的拦截器如何注入Web的服务,以及EfCore如何自定义查询,我就看了一下EfCore的源码,在此之前我针对asp.net web 做了一个源码解读,有兴趣的朋友可以看前面的文章,也给别人说过啥时候讲解一下efcore的源码,刚好借助这么一个机会,讲一讲EfCore的源码,本篇文章作为一个开端,会呈现一下几点

    一:首先是AddDbContext里面做了什么。

    二:DbContext的构造函数里面做了那些事情。

    三:如何在EfCore的服务中获取到Web注入的服务的方式之一。

    四:拦截查询的几种方式。

    五:使用缓存查询方法提升性能。

    六:如何托管EFCORE的IOC容器(和Web的IOC使用同一个)

    以上作为本篇文章的所有内容,接下来,我们来开始讲解源码,动手实践。

AddDbContext

    EfCore提供了AddDbContext,AddDbContextFactory,AddDbContextPool,AddPooledDbContextFactory这几种扩展方法,我们会依次讲解,首先会讲解AddDbContext,后续的文章会依次讲解其余的方法。话不多说,上源码。下面是AddDbContext的源码,提供了多种方法,但是最终都会调用到这里,第一个参数是一个设置OptionBuilder的委托,传入了ServiceProvider和OptionBuilder,第二,三个分别是DbContext和DBContextOption的生命周期。

    在下面的代码,刚开始判断了如果DBContext的生命周期是单例,要将Option的生命周期也设置为单例,如果不设置为单例,就会出现错误,这个错误在之前讲解IOC的文章中,我记得也提到过,接下来判断设置Option的委托是否为null,如果不为null,那DBContext的构造函数是必须要有一个参数,所以下面调用了一个方法CheckContextConstructors。

  1. public static IServiceCollection AddDbContext<TContextService, TContextImplementation>(
  2. this IServiceCollection serviceCollection,
  3. Action<IServiceProvider, DbContextOptionsBuilder>? optionsAction,
  4. ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
  5. ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
  6. where TContextImplementation : DbContext, TContextService
  7. {
  8. if (contextLifetime == ServiceLifetime.Singleton)
  9. {
  10. optionsLifetime = ServiceLifetime.Singleton;
  11. }
  12. if (optionsAction != null)
  13. {
  14. CheckContextConstructors<TContextImplementation>();
  15. }
  16. AddCoreServices<TContextImplementation>(serviceCollection, optionsAction, optionsLifetime);
  17. if (serviceCollection.Any(d => d.ServiceType == typeof(IDbContextFactorySource<TContextImplementation>)))
  18. {
  19. // Override registration made by AddDbContextFactory
  20. var serviceDescriptor = serviceCollection.FirstOrDefault(d => d.ServiceType == typeof(TContextImplementation));
  21. if (serviceDescriptor != null)
  22. {
  23. serviceCollection.Remove(serviceDescriptor);
  24. }
  25. }
  26. serviceCollection.TryAdd(new ServiceDescriptor(typeof(TContextService), typeof(TContextImplementation), contextLifetime));
  27. if (typeof(TContextService) != typeof(TContextImplementation))
  28. {
  29. serviceCollection.TryAdd(
  30. new ServiceDescriptor(
  31. typeof(TContextImplementation),
  32. p => (TContextImplementation)p.GetService<TContextService>()!,
  33. contextLifetime));
  34. }
  35. return serviceCollection;
  36. }

  1. private static void CheckContextConstructors<TContext>()
  2. where TContext : DbContext
  3. {
  4. var declaredConstructors = typeof(TContext).GetTypeInfo().DeclaredConstructors.ToList();
  5. if (declaredConstructors.Count == 1
  6. && declaredConstructors[0].GetParameters().Length == 0)
  7. {
  8. throw new ArgumentException(CoreStrings.DbContextMissingConstructor(typeof(TContext).ShortDisplayName()));
  9. }
  10. }

    在CheckContextConstructors,我们看到反射去获取DBContext的继承类,查找构造函数,并且参数如果是0就会报异常。接下来在往下走,调用了一个AddCoreServices的方法,在这个方法里,我们是将DBContextOptions的泛型和非泛型注入到容器里面去,其中有一个CreateDbContextOptions的方法,里面去new了一个DbContextOptionsBuilder类,然后调用了一个UseApplicationServiceProvider方法,

  1. private static void AddCoreServices<TContextImplementation>(
  2. IServiceCollection serviceCollection,
  3. Action<IServiceProvider, DbContextOptionsBuilder>? optionsAction,
  4. ServiceLifetime optionsLifetime)
  5. where TContextImplementation : DbContext
  6. {
  7. serviceCollection.TryAdd(
  8. new ServiceDescriptor(
  9. typeof(DbContextOptions<TContextImplementation>),
  10. p => CreateDbContextOptions<TContextImplementation>(p, optionsAction),
  11. optionsLifetime));
  12. serviceCollection.Add(
  13. new ServiceDescriptor(
  14. typeof(DbContextOptions),
  15. p => p.GetRequiredService<DbContextOptions<TContextImplementation>>(),
  16. optionsLifetime));
  17. }
  18. private static DbContextOptions<TContext> CreateDbContextOptions<TContext>(
  19. IServiceProvider applicationServiceProvider,
  20. Action<IServiceProvider, DbContextOptionsBuilder>? optionsAction)
  21. where TContext : DbContext
  22. {
  23. var builder = new DbContextOptionsBuilder<TContext>(
  24. new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>()));
  25. builder.UseApplicationServiceProvider(applicationServiceProvider);
  26. optionsAction?.Invoke(applicationServiceProvider, builder);
  27. return builder.Options;
  28. }

    下面是一个UseApplicationServiceProvider的调用链,最终调用到了WithApplicationServiceProvider方法,可以看到是返回了一个CoreOptionsExtension,最终调用WithOptions添加到了DbContextOptionsBuilder里面去,上面的代码中,我们new了一个DbContextOptionsBuilder,里面传入了一个DbContextOptions,构造函数传入了new Dictionary<Type,IDbContextOptionsExtension>(),最终我们的CoreOptionsExtension会添加到我们传入的这个字典里,用来保存所有的IDbContextOptionsExtension,这个接口可以理解为,数据库Options的扩展,接口定义如下,Info是关于扩展的一些元数据信息,ApplyService方法,参数是一个IServiceCollection,这个方法是我们将我们要注入的服务注入到这个里面去,因为EfCore的IOC和Web的IOC是区分开的,所以使用了不同的IServiceCollection,虽然提供了UseApplicationServiceProvider和UseInternalServiceProvider方法,实际上并不能实现IOC接管,设计实在是鸡肋,待会到了DbContext的构造函数中我们会看到为什么说鸡肋。Validate方法则是验证当前扩展,例如你得这个实现里面有一些参数,是必须要配置,或者配置有一个规则,我们在这里验证我们的配置或者规则是否符合我们需要的数据,如果不符合,在这里可以直接抛出异常。

    回到CreateDbContextOptions,此时我们可以确保我们的Option的Extension里面是有一个CoreOptionsExtension,接下来,判断有没有设置OptionsBuilder的委托,调用然后返回到AddDbContext。

  1. public new virtual DbContextOptionsBuilder<TContext> UseApplicationServiceProvider(IServiceProvider? serviceProvider)
  2. => (DbContextOptionsBuilder<TContext>)base.UseApplicationServiceProvider(serviceProvider);
  3. public virtual DbContextOptionsBuilder UseApplicationServiceProvider(IServiceProvider? serviceProvider)
  4. => WithOption(e => e.WithApplicationServiceProvider(serviceProvider));
  5. public virtual CoreOptionsExtension WithApplicationServiceProvider(IServiceProvider? applicationServiceProvider)
  6. {
  7. var clone = Clone();
  8. clone._applicationServiceProvider = applicationServiceProvider;
  9. return clone;
  10. }
  1. private DbContextOptionsBuilder WithOption(Func<CoreOptionsExtension, CoreOptionsExtension> withFunc)
  2. {
  3. ((IDbContextOptionsBuilderInfrastructure)this).AddOrUpdateExtension(
  4. withFunc(Options.FindExtension<CoreOptionsExtension>() ?? new CoreOptionsExtension()));
  5. return this;
  6. }
  1. public interface IDbContextOptionsExtension
  2. {
  3. /// <summary>
  4. /// Information/metadata about the extension.
  5. /// </summary>
  6. DbContextOptionsExtensionInfo Info { get; }
  7. /// <summary>
  8. /// Adds the services required to make the selected options work. This is used when there
  9. /// is no external <see cref="IServiceProvider" /> and EF is maintaining its own service
  10. /// provider internally. This allows database providers (and other extensions) to register their
  11. /// required services when EF is creating an service provider.
  12. /// </summary>
  13. /// <param name="services">The collection to add services to.</param>
  14. void ApplyServices(IServiceCollection services);
  15. /// <summary>
  16. /// Gives the extension a chance to validate that all options in the extension are valid.
  17. /// Most extensions do not have invalid combinations and so this will be a no-op.
  18. /// If options are invalid, then an exception should be thrown.
  19. /// </summary>
  20. /// <param name="options">The options being validated.</param>
  21. void Validate(IDbContextOptions options);
  22. }
  1. void IDbContextOptionsBuilderInfrastructure.AddOrUpdateExtension<TExtension>(TExtension extension)
  2. => _options = _options.WithExtension(extension);

    在AddDbContext的最后,这几行代码,是将我们的DbContext注入到我们的IOC容器中去。

  1. if (serviceCollection.Any(d => d.ServiceType == typeof(IDbContextFactorySource<TContextImplementation>)))
  2. {
  3. // Override registration made by AddDbContextFactory
  4. var serviceDescriptor = serviceCollection.FirstOrDefault(d => d.ServiceType == typeof(TContextImplementation));
  5. if (serviceDescriptor != null)
  6. {
  7. serviceCollection.Remove(serviceDescriptor);
  8. }
  9. }
  10. serviceCollection.TryAdd(new ServiceDescriptor(typeof(TContextService), typeof(TContextImplementation), contextLifetime));
  11. if (typeof(TContextService) != typeof(TContextImplementation))
  12. {
  13. serviceCollection.TryAdd(
  14. new ServiceDescriptor(
  15. typeof(TContextImplementation),
  16. p => (TContextImplementation)p.GetService<TContextService>()!,
  17. contextLifetime));
  18. }

    至此,关于AddDbContext的源码讲解完毕,接下来进到DbContext构造函数的源码讲解,这里设计内容稍微多一些。

DBContext构造

    构造函数的代码是整体是没多少的,但是最重要的还是在于GetOrAdd的方法。所以这里我会只讲这个方法的内部

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

    下面是ServiceProviderCache的全部源码,在GetOrAdd方法,先获取了CoreOptionsExtension,这个我们在AddDbContext的时候,已经添加过了,并且设置了ApplicationProvider,在往下走,判断InternalServiceProvider,这里我们没有设置,就会继续往下走,判断了一个ServiceProviderCachingEnabled 这个默认是true,然后下面获取CoreOptionsExtension的ApplicationServiceProvider,还记得我们在最初的时候AddDbContext的时候,创建DbContextOptionsBuilder的时候,我们UseApplicationServiceProvider方法,就是设置了一个公用的Provider,现在把他在设置为null,如果说这个Provider有啥作用,哈哈哈哈,我认为他就是创建Options的时候需要用,然后给一个有东西的不为空的CoreOptionsExtension,这个方法,实际上我觉得微软设置为internal最好了,这样可能会存在误解开发者,而InternalServiceProvider是很有用,可以和我们的web共用一个ioc容器,在本文的最后,我会将ef的ioc容器托管到web的。

    ServiceProviderCachingEnabled参数代表是否将GetOrAdd通过BuildServiceProvider创建的ServiceProvider缓存到_configuration中去,不缓存的话,每次都是一个新的Provider,对性能有影响,如果缓存,则第一次创建后面都是从那里面取。

    接下来就到了GetOrAdd最后,要调用BuildServiceProvider方法来创建一个ServiceProvider,下面的方法,我们着重看几个关键点一个是ApplyService,一个是new ServiceCollection(说明web的ioc和ef的ioc不是同一个),ReplaceService。

  1. public class ServiceProviderCache
  2. {
  3. private readonly ConcurrentDictionary<IDbContextOptions, (IServiceProvider ServiceProvider, IDictionary<string, string> DebugInfo)>
  4. _configurations = new();
  5. /// <summary>
  6. /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
  7. /// the same compatibility standards as public APIs. It may be changed or removed without notice in
  8. /// any release. You should only use it directly in your code with extreme caution and knowing that
  9. /// doing so can result in application failures when updating to a new Entity Framework Core release.
  10. /// </summary>
  11. public static ServiceProviderCache Instance { get; } = new();
  12. /// <summary>
  13. /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
  14. /// the same compatibility standards as public APIs. It may be changed or removed without notice in
  15. /// any release. You should only use it directly in your code with extreme caution and knowing that
  16. /// doing so can result in application failures when updating to a new Entity Framework Core release.
  17. /// </summary>
  18. public virtual IServiceProvider GetOrAdd(IDbContextOptions options, bool providerRequired)
  19. {
  20. var coreOptionsExtension = options.FindExtension<CoreOptionsExtension>();
  21. var internalServiceProvider = coreOptionsExtension?.InternalServiceProvider;
  22. if (internalServiceProvider != null)
  23. {
  24. ValidateOptions(options);
  25. var optionsInitializer = internalServiceProvider.GetService<ISingletonOptionsInitializer>();
  26. if (optionsInitializer == null)
  27. {
  28. throw new InvalidOperationException(CoreStrings.NoEfServices);
  29. }
  30. if (providerRequired)
  31. {
  32. optionsInitializer.EnsureInitialized(internalServiceProvider, options);
  33. }
  34. return internalServiceProvider;
  35. }
  36. if (coreOptionsExtension?.ServiceProviderCachingEnabled == false)
  37. {
  38. return BuildServiceProvider(options, (_configurations, options)).ServiceProvider;
  39. }
  40. var cacheKey = options;
  41. var extension = options.FindExtension<CoreOptionsExtension>();
  42. if (extension?.ApplicationServiceProvider != null)
  43. {
  44. cacheKey = ((DbContextOptions)options).WithExtension(extension.WithApplicationServiceProvider(null));
  45. }
  46. return _configurations.GetOrAdd(
  47. cacheKey,
  48. static (contextOptions, tuples) => BuildServiceProvider(contextOptions, tuples), (_configurations, options))
  49. .ServiceProvider;
  50. static (IServiceProvider ServiceProvider, IDictionary<string, string> DebugInfo) BuildServiceProvider(
  51. IDbContextOptions _,
  52. (ConcurrentDictionary<IDbContextOptions, (IServiceProvider ServiceProvider, IDictionary<string, string> DebugInfo)>,
  53. IDbContextOptions) arguments)
  54. {
  55. var (configurations, options) = arguments;
  56. ValidateOptions(options);
  57. var debugInfo = new Dictionary<string, string>();
  58. foreach (var optionsExtension in options.Extensions)
  59. {
  60. optionsExtension.Info.PopulateDebugInfo(debugInfo);
  61. }
  62. debugInfo = debugInfo.OrderBy(_ => debugInfo.Keys).ToDictionary(d => d.Key, v => v.Value);
  63. var services = new ServiceCollection();
  64. var hasProvider = ApplyServices(options, services);
  65. var replacedServices = options.FindExtension<CoreOptionsExtension>()?.ReplacedServices;
  66. if (replacedServices != null)
  67. {
  68. var updatedServices = new ServiceCollection();
  69. foreach (var descriptor in services)
  70. {
  71. if (replacedServices.TryGetValue((descriptor.ServiceType, descriptor.ImplementationType), out var replacementType))
  72. {
  73. ((IList<ServiceDescriptor>)updatedServices).Add(
  74. new ServiceDescriptor(descriptor.ServiceType, replacementType, descriptor.Lifetime));
  75. }
  76. else if (replacedServices.TryGetValue((descriptor.ServiceType, null), out replacementType))
  77. {
  78. ((IList<ServiceDescriptor>)updatedServices).Add(
  79. new ServiceDescriptor(descriptor.ServiceType, replacementType, descriptor.Lifetime));
  80. }
  81. else
  82. {
  83. ((IList<ServiceDescriptor>)updatedServices).Add(descriptor);
  84. }
  85. }
  86. services = updatedServices;
  87. }
  88. var serviceProvider = services.BuildServiceProvider();
  89. if (hasProvider)
  90. {
  91. serviceProvider
  92. .GetRequiredService<ISingletonOptionsInitializer>()
  93. .EnsureInitialized(serviceProvider, options);
  94. }
  95. using (var scope = serviceProvider.CreateScope())
  96. {
  97. var scopedProvider = scope.ServiceProvider;
  98. // If loggingDefinitions is null, then there is no provider yet
  99. var loggingDefinitions = scopedProvider.GetService<LoggingDefinitions>();
  100. if (loggingDefinitions != null)
  101. {
  102. // Because IDbContextOptions cannot yet be resolved from the internal provider
  103. var logger = new DiagnosticsLogger<DbLoggerCategory.Infrastructure>(
  104. ScopedLoggerFactory.Create(scopedProvider, options),
  105. scopedProvider.GetRequiredService<ILoggingOptions>(),
  106. scopedProvider.GetRequiredService<DiagnosticSource>(),
  107. loggingDefinitions,
  108. new NullDbContextLogger());
  109. if (configurations.IsEmpty)
  110. {
  111. logger.ServiceProviderCreated(serviceProvider);
  112. }
  113. else
  114. {
  115. logger.ServiceProviderDebugInfo(
  116. debugInfo,
  117. configurations.Values.Select(v => v.DebugInfo).ToList());
  118. if (configurations.Count >= 20)
  119. {
  120. logger.ManyServiceProvidersCreatedWarning(
  121. configurations.Values.Select(e => e.ServiceProvider).ToList());
  122. }
  123. }
  124. var applicationServiceProvider = options.FindExtension<CoreOptionsExtension>()?.ApplicationServiceProvider;
  125. if (applicationServiceProvider?.GetService<IRegisteredServices>() != null)
  126. {
  127. logger.RedundantAddServicesCallWarning(serviceProvider);
  128. }
  129. }
  130. }
  131. return (serviceProvider, debugInfo);
  132. }
  133. }
  134. private static void ValidateOptions(IDbContextOptions options)
  135. {
  136. foreach (var extension in options.Extensions)
  137. {
  138. extension.Validate(options);
  139. }
  140. }
  141. private static bool ApplyServices(IDbContextOptions options, ServiceCollection services)
  142. {
  143. var coreServicesAdded = false;
  144. foreach (var extension in options.Extensions)
  145. {
  146. extension.ApplyServices(services);
  147. if (extension.Info.IsDatabaseProvider)
  148. {
  149. coreServicesAdded = true;
  150. }
  151. }
  152. if (coreServicesAdded)
  153. {
  154. return true;
  155. }
  156. new EntityFrameworkServicesBuilder(services).TryAddCoreServices();
  157. return false;
  158. }
  159. }

     我们先看ApplyService,在这个方法我们看到,是传入了Options,ServiceCollection,然后循环遍历Options的Extensions,调用他的ApplyService方法,传入serviceCollection,下图,我们看到默认是有这么多的实现,根据你在对DBContextOptionsBuilder提供的方法的使用,会给Options的Extensions添加不同的Extension,最后调用各自的ApplyService,我们找一个看看具体在做什么事情。哈哈,不用猜,大家也知道,肯定是注入服务,通过options的Extensions附加一些其他关于EF的功能,并且将他们所需要的服务注入到传入的ServiceCollection里面,其他的都是一样,所以在我们没有托管ef的ioc到web的时候可以使用这种方式来实现,后面也会写一个这样的例子。最后调用一下TryAddCoreService方法,这个方法有许多EF需用到重要的服务的注入。

    而ReplaceService就是我们在调用DbContextOptionsBuilder的ReplaceService<>方法的时候里面保存的我们要替换的类型,以及实现,在这里重新注入到容器里,用上面的代码结合看,就是ApplyService先注入一遍,然后在替换一下,最后调用一下BuildServiceProvider方法生成一个ServiceProvider,在后面的代码就是一些日志相关的配置,此处就不过多讲解。

  1. public virtual void ApplyServices(IServiceCollection services)
  2. => services.AddEntityFrameworkProxies();

 

 
    在上面的讲解中,我们有几个可以自定义的点就是一个是IDbContextOptionsExtension,这个我们可以在不托管ef的ioc到web的ioc的时候,我们可以实现一个这个接口,然后在代码添加到Extension就可以注入EF所需要用到的服务。接下来在下面段落,我会写一个简单的例子来注入我们要的服务。(不托管ioc到web的方式)。

EFCore服务注入

    先上代码,代码没有多少,就是实现这个接口,定义一个Inject特性,用来标记从Web的IOC我们需要检索那些接口注入到EF的ioc中去,这样做有一个弊端就是Web的会注入一遍,Ef也会注入一遍,重复注入,在Program.cs里面我们先注入一个返回IServiceCollection的Func,这样在DBContext可以获取到这个 传到ServiceExtension里面,就可以拿到Web的IOC注入的服务。

  1. builder.Services.AddScoped<IWebGetName, WebGetName>();
  2. builder.Services.AddSingleton(() => builder.Services);
  1. [InjectAttribute]
  2. public interface IWebGetName
  3. {
  4. public string GetName();
  5. }
  1. [AttributeUsage(AttributeTargets.Interface| AttributeTargets.Class)]
  2. public class InjectAttribute:Attribute
  3. {
  4. public InjectAttribute()
  5. {
  6. }
  7. }

 

  1. public class ServiceExtension : IDbContextOptionsExtension
  2. {
  3. public ServiceExtension(Func<IServiceCollection> func)
  4. {
  5. Func = func;
  6. }
  7. public DbContextOptionsExtensionInfo Info => new ExtensionInfo(this);
  8. public Func<IServiceCollection> Func { get; }
  9. public void ApplyServices(IServiceCollection services)
  10. {
  11. var ser=Func();
  12. var type = ser.Where(s => s.ServiceType?.GetCustomAttribute<InjectAttribute>() != null || s.ImplementationType?.GetCustomAttribute<InjectAttribute>() != null).ToList();
  13. foreach (var item in type)
  14. {
  15. services.TryAdd(new ServiceDescriptor(item.ServiceType, item.ImplementationType, item.Lifetime));
  16. }
  17. services.AddScoped<IDBGetName, DBGetName>();
  18. }
  19. public void Validate(IDbContextOptions options)
  20. {
  21. }
  22. }
  23. public class ExtensionInfo: DbContextOptionsExtensionInfo
  24. {
  25. public ExtensionInfo(IDbContextOptionsExtension extension):base(extension)
  26. {
  27. }
  28. public override bool IsDatabaseProvider => false;
  29. public override string LogFragment => string.Empty;
  30. public override int GetServiceProviderHashCode()
  31. {
  32. return 0;
  33. }
  34. public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
  35. {
  36. }
  37. public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
  38. {
  39. return true;
  40. }
  41. }

    接口定义好之后,服务也注入进去,接下来就是在DBContext里面添加这个扩展,因为optionsBuilder.Options只读,所以我们添加Extension就需要用AddOrUpdateExtension的方法来添加,因为Options在DbContextOptionsBuilder的内部字段是可以更改的。接下来扩展添加进去之后,我们运行程序,获取一个DBContext,然后就会走到这里添加我们的扩展,从而注入我们注入的IWebGetName,就可以在EF的IOC获取我们web注入服务。

  1. builder.Services.AddDbContext<DecodeMicroMsgContext>((a, m) => {
  2. ((IDbContextOptionsBuilderInfrastructure)m).AddOrUpdateExtension(new ServiceExtension(a.GetService<Func<IServiceCollection>>()));
  3. m.UseSqlite("Data Source=C:\\Users\\Chenxd\\Desktop\\资料\\CrackMsg\\CrackDb\\CrackDb\\bin\\Debug\\net7.0-windows\\TOOLS\\output\\decode_MicroMsg.db;");
  4. });

拦截查询

  Sql拦截

    针对SQL拦截,这里我会直接贴上我之前有一篇文章aop的代码,来作为讲解,其中有用到了DBInterceptor作为拦截器拦截DBCommand进行sql拦截,实现读写分离的方式,下面的代码是我自己实现了DBCommandInterceptor来实现的一个拦截器,在DBContext中将拦截器添加进去,在每次执行查询或者增加删除修改的时候,都会进入这个拦截器,从而实现自己想要的业务逻辑,我在此处是写了一个简单的读写分离,感兴趣的可以看看之前的文章https://www.cnblogs.com/1996-Chinese-Chen/p/15776120.html这个文章的代码地址已经失效,最后我会将本例程的所有代码放在百度网盘,其中包括这个AOP的代码,感兴趣的朋友可以下载看看。

  1. var list=new List<IInterceptor>();
  2. list.Add(new DbContextInterceptor());
  3. optionsBuilder.AddInterceptors(list);
  1. public class DbContextInterceptor:DbCommandInterceptor
  2. {
  3. private DbConnection _connection;
  4. private DbCommand _command;
  5. private CommandSource _commandSource;
  6. public DbContextInterceptor()
  7. {
  8. }
  9. public override DbCommand CommandCreated(CommandEndEventData eventData, DbCommand result)
  10. {
  11. return _command;
  12. }
  13. public override InterceptionResult<DbCommand> CommandCreating(CommandCorrelatedEventData eventData, InterceptionResult<DbCommand> result)
  14. {
  15. _commandSource = eventData.CommandSource;
  16. if (eventData.CommandSource==CommandSource.LinqQuery)
  17. {
  18. _connection = new MySqlConnection(eventData.Connection.ConnectionString);
  19. _command = new MySqlCommand();
  20. }
  21. return InterceptionResult<DbCommand>.SuppressWithResult(_command);
  22. }
  23. public override void CommandFailed(DbCommand command, CommandErrorEventData eventData)
  24. {
  25. base.CommandFailed(command, eventData);
  26. }
  27. public override Task CommandFailedAsync(DbCommand command, CommandErrorEventData eventData, CancellationToken cancellationToken = default)
  28. {
  29. return base.CommandFailedAsync(command, eventData, cancellationToken);
  30. }
  31. public override InterceptionResult DataReaderDisposing(DbCommand command, DataReaderDisposingEventData eventData, InterceptionResult result)
  32. {
  33. return base.DataReaderDisposing(command, eventData, result);
  34. }
  35. public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
  36. {
  37. return base.NonQueryExecuted(command, eventData, result);
  38. }
  39. public override ValueTask<int> NonQueryExecutedAsync(DbCommand command, CommandExecutedEventData eventData, int result, CancellationToken cancellationToken = default)
  40. {
  41. return base.NonQueryExecutedAsync(command, eventData, result, cancellationToken);
  42. }
  43. public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
  44. {
  45. return base.NonQueryExecuting(command, eventData, result);
  46. }
  47. public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
  48. {
  49. return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
  50. }
  51. public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
  52. {
  53. return base.ReaderExecuted(command, eventData, result);
  54. }
  55. public override ValueTask<DbDataReader> ReaderExecutedAsync(DbCommand command, CommandExecutedEventData eventData, DbDataReader result, CancellationToken cancellationToken = default)
  56. {
  57. return base.ReaderExecutedAsync(command, eventData, result, cancellationToken);
  58. }
  59. public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
  60. {
  61. command.CommandText = "";
  62. return base.ReaderExecuting(command, eventData, result);
  63. }
  64. public async override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
  65. {
  66. InterceptionResult<DbDataReader> results;
  67. if (_commandSource == CommandSource.LinqQuery)
  68. {
  69. var connect = new MySqlConnection("data source=192.168.21.129;database=MasterSlave; userid=root;pwd=199645; charset=utf8;ConvertZeroDateTime=True;pooling=true; allowuservariables=true;");
  70. connect.Open();
  71. _command = new MySqlCommand(command.CommandText, connect);
  72. var reader = await _command.ExecuteReaderAsync();
  73. results = InterceptionResult<DbDataReader>.SuppressWithResult(reader);
  74. }
  75. else
  76. {
  77. results=await base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
  78. }
  79. return results;
  80. }
  81. public override object ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object result)
  82. {
  83. return base.ScalarExecuted(command, eventData, result);
  84. }
  85. public override ValueTask<object> ScalarExecutedAsync(DbCommand command, CommandExecutedEventData eventData, object result, CancellationToken cancellationToken = default)
  86. {
  87. return base.ScalarExecutedAsync(command, eventData, result, cancellationToken);
  88. }
  89. public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
  90. {
  91. return base.ScalarExecuting(command, eventData, result);
  92. }
  93. public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default)
  94. {
  95. return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
  96. }
  97. }

  表达式拦截

    上面我们讲了SQL拦截,接下来我们讲一下表达式拦截,我们都知道,EF的核心在于表达式树,可以说表达式树构造了整个EF的核心,关于表达式树,我在我的第一篇博客就写了很多关于表达式树的案例,https://www.cnblogs.com/1996-Chinese-Chen/p/14987967.html,感兴趣的朋友可以看看,所以此处表达式树我不会做讲解,只有如何实现自定义的表达式树拦截,

    重要的有三个我们需要实现的接口,一个是IQueryable,IQueryCompiler,还有一个IAsyncQueryProvider,通过实现这三个接口,外加IDatabase,IQueryContextFactory需要用到的两个接口,实际上只是表达式拦截,只需要实现一个IQueryCompiler也可以实现,我i自己是实现了这三个,最主要的还是在IQueryCompiler,接下来看看具体的实现代码。

    IQueryCompiler

  1. public class TestQueryCompiler : IQueryCompiler
  2. {
  3. public TestQueryCompiler(IDatabase database, IQueryContextFactory queryContextFactory)
  4. {
  5. Database = database;
  6. var a = Database.GetType();
  7. QueryContextFactory = queryContextFactory;
  8. }
  9. public IDatabase Database { get; }
  10. public IQueryContextFactory QueryContextFactory { get; }
  11. public Func<QueryContext, TResult> CreateCompiledAsyncQuery<TResult>(Expression query)
  12. {
  13. var queryFunc = Database.CompileQuery<TResult>(query, true);
  14. return queryFunc;
  15. }
  16. public Func<QueryContext, TResult> CreateCompiledQuery<TResult>(Expression query)
  17. {
  18. var queryFunc = Database.CompileQuery<TResult>(query, false);
  19. return queryFunc;
  20. }
  21. public TResult Execute<TResult>(Expression query)
  22. {
  23. var queryFunc = Database.CompileQuery<TResult>(query, false);
  24. var res = queryFunc(QueryContextFactory.Create());
  25. return res;
  26. }
  27. public TResult ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken)
  28. {
  29. var queryFunc = Database.CompileQuery<TResult>(query, true);
  30. var res = queryFunc(QueryContextFactory.Create());
  31. return res;
  32. }
  33. }

    IAsyncQueryProvider

    

  1. public class TestQueryProvider : IAsyncQueryProvider
  2. {
  3. public TestQueryProvider(IDBGetName ta, IQueryCompiler query,IWebGetName webGetName)
  4. {
  5. Ta = ta;
  6. Query = query;
  7. WebGetName = webGetName;
  8. }
  9. public IDBGetName Ta { get; }
  10. public IQueryCompiler Query { get; }
  11. public IWebGetName WebGetName { get; }
  12. public IQueryable CreateQuery(Expression expression)
  13. {
  14. return new Queryable<object>(this, expression);
  15. }
  16. public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
  17. {
  18. return new Queryable<TElement>(this, expression);
  19. }
  20. public object? Execute(Expression expression)
  21. {
  22. return Query.Execute<object>(expression);
  23. }
  24. public TResult Execute<TResult>(Expression expression)
  25. {
  26. return Query.Execute<TResult>(expression);
  27. }
  28. public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
  29. {
  30. return Query.ExecuteAsync<TResult>(expression, cancellationToken);
  31. }
  32. }  

    IQueryable

  1. public class Queryable<T> : IQueryable<T>
  2. {
  3. public Queryable(IAsyncQueryProvider queryProvider, Expression expression)
  4. {
  5. QueryProvider = queryProvider;
  6. Expression = expression;
  7. }
  8. public Type ElementType => typeof(T);
  9. public IAsyncQueryProvider QueryProvider { get; }
  10. public Expression Expression { get; }
  11. public IQueryProvider Provider => QueryProvider;
  12. public IEnumerator GetEnumerator()
  13. {
  14. return Provider.Execute<IEnumerable>(Expression).GetEnumerator();
  15. }
  16. IEnumerator<T> IEnumerable<T>.GetEnumerator()
  17. {
  18. return Provider.Execute<IEnumerable<T>>(Expression).GetEnumerator();
  19. }
  20. }

    在实现了那三个接口之后,我们就需要将我们的服务使用DBContextOptionsBuilder的ReplaceService替换掉,这样,在执行查询的时候就会走我们创建的TestQueryProvider,然后我们在这个类里调用关于Queryable和TestQueryCompiler来执行查询,如果又需要修改,也可以修改Expression从而达到拦截修改。我们最终是需要借助IDataBase的CompileQuery方法来实现构建查询的委托,从而实现查询,在底层还有Visitor遍历表达式树,当然了,此处我只展示一个拦截表达式树,后续的源码讲解会看到,欢迎大家关注。

    如果是使用了EF的IOC托管到了Web的IOC,只需要正常注入服务就行,生命周期是Scope,

  1. #未接管
    optionsBuilder.ReplaceService<IAsyncQueryProvider,TestQueryProvider >();
  2. optionsBuilder.ReplaceService<IQueryCompiler,TestQueryCompiler>();
    #接管IOC

   builder.Services.AddScoped<IAsyncQueryProvider, TestQueryProvider>();
   builder.Services.AddScoped<IQueryCompiler, TestQueryCompiler>(s =>
   {
   var database = s.GetService<IDatabase>();
   var factory = s.GetService<IQueryContextFactory>();
   return new TestQueryCompiler(database, factory);
   });

 

 

 缓存查询方法

    在上面的代码中,我们可以看到我们调用了一个ComileQuery方法,构建了一个委托,实际上,我们在业务编码中,也可以使用缓存查询,来提升业务系统的性能,虽然我们不能使用IDataBase的这个发给发,但是EF提供了一个静态类,里面的ComileQuery方法支持构建查询的委托,

     看下面代码,我们可以调用这个方法缓存一个查询的方法,后面就不会再去调用很多的类,很多的方法来实现我们的查询,可以缓存起来,来提升我们的查询性能,同时这个ComileQuery方法最终也会调用到我们上面自定义的TestQueryCompiler的ComileQuery方法去,并且同步对同步,异步对异步.

  1. public class WeatherForecastController : ControllerBase
  2. {
  3. private static readonly string[] Summaries = new[]
  4. {
  5. "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
  6. };
  7. private Func<DecodeMicroMsgContext, IQueryable<Contact>> Func;
  8. private readonly ILogger<WeatherForecastController> _logger;
  9. private readonly IWebGetName webGetName;
  10. public WeatherForecastController(ILogger<WeatherForecastController> logger,IWebGetName webGetName, DecodeMicroMsgContext dbContext)
  11. {
  12. _logger = logger;
  13. this.webGetName = webGetName;
  14. DbContext = dbContext;
  15. }
  16. public DecodeMicroMsgContext DbContext { get; }
  17. [HttpGet(Name = "GetWeatherForecast")]
  18. public List<Contact> Get()
  19. {
  20. Func = EF.CompileQuery<DecodeMicroMsgContext, IQueryable<Contact>>(s => s.Contacts.Take(10));
  21. return DbContext.Contacts.Take(10).ToList();
  22. }

EF的IOC托管到WEB的IOC

    在上面讲源码的时候,我们提到了一个方法,叫UseInternalServiceProvider,我们需要借助这个方法来把EF的ioc托管到web,同样是DBContextOptionsBuilder的方法,将web的ServiceProvider获取到并且调用这个方法,同时,我们还需要在Program.cs调用一个方法  builder.Services.AddEntityFrameworkSqlite();如果是其他数据库也是一样的道理,需要调用这样的方法,可以看第三方提供的库源码,或者文档,这里我是SQLITE数据库,就调用这个方法,这个方法是将SqlLite的一些服务注入到容器里,并且将我们的web容器传入到EF里面去,这样EF注入的服务是和Web注入的服务是在一起的,然后在调用UseInternalServiceProvider指定ServiceProvider就可以实现EF和WEB共用一个IOC。

    下面是Program的Main方法的所有代码。

  1. public static void Main(string[] args)
  2. {
  3. var builder = WebApplication.CreateBuilder(args);
  4. builder.Services.AddScoped<IWebGetName,WebGetName>();
  5. builder.Services.AddScoped<IAsyncQueryProvider, TestQueryProvider>();
  6. builder.Services.AddScoped<IQueryCompiler, TestQueryCompiler>(s =>
  7. {
  8. var database = s.GetService<IDatabase>();
  9. var factory = s.GetService<IQueryContextFactory>();
  10. return new TestQueryCompiler(database, factory);
  11. });
  12. builder.Services.AddScoped<IDBGetName, DBGetName>();
  13. builder.Services.AddEntityFrameworkSqlite();
  14. builder.Services.AddDbContext<DecodeMicroMsgContext>((a, m) => {
  15. m.UseSqlite("Data Source=C:\\Users\\Chenxd\\Desktop\\资料\\CrackMsg\\CrackDb\\CrackDb\\bin\\Debug\\net7.0-windows\\TOOLS\\output\\decode_MicroMsg.db;");
  16. });
  17. builder.Services.AddControllers();
  18. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
  19. builder.Services.AddEndpointsApiExplorer();
  20. builder.Services.AddSwaggerGen();
  21. builder.Services.AddSingleton(() => builder.Services);
  22. var app = builder.Build();
  23. // Configure the HTTP request pipeline.
  24. if (app.Environment.IsDevelopment())
  25. {
  26. app.UseSwagger();
  27. app.UseSwaggerUI();
  28. }
  29. app.UseAuthorization();
  30. app.MapControllers();
  31. app.Run();
  32. }

 

  1. public static IServiceCollection AddEntityFrameworkSqlite(this IServiceCollection serviceCollection)
  2. {
  3. var builder = new EntityFrameworkRelationalServicesBuilder(serviceCollection)
  4. .TryAdd<LoggingDefinitions, SqliteLoggingDefinitions>()
  5. .TryAdd<IDatabaseProvider, DatabaseProvider<SqliteOptionsExtension>>()
  6. .TryAdd<IRelationalTypeMappingSource, SqliteTypeMappingSource>()
  7. .TryAdd<ISqlGenerationHelper, SqliteSqlGenerationHelper>()
  8. .TryAdd<IRelationalAnnotationProvider, SqliteAnnotationProvider>()
  9. .TryAdd<IModelValidator, SqliteModelValidator>()
  10. .TryAdd<IProviderConventionSetBuilder, SqliteConventionSetBuilder>()
  11. .TryAdd<IModificationCommandBatchFactory, SqliteModificationCommandBatchFactory>()
  12. .TryAdd<IRelationalConnection>(p => p.GetRequiredService<ISqliteRelationalConnection>())
  13. .TryAdd<IMigrationsSqlGenerator, SqliteMigrationsSqlGenerator>()
  14. .TryAdd<IRelationalDatabaseCreator, SqliteDatabaseCreator>()
  15. .TryAdd<IHistoryRepository, SqliteHistoryRepository>()
  16. .TryAdd<IRelationalQueryStringFactory, SqliteQueryStringFactory>()
  17. .TryAdd<IMethodCallTranslatorProvider, SqliteMethodCallTranslatorProvider>()
  18. .TryAdd<IAggregateMethodCallTranslatorProvider, SqliteAggregateMethodCallTranslatorProvider>()
  19. .TryAdd<IMemberTranslatorProvider, SqliteMemberTranslatorProvider>()
  20. .TryAdd<IQuerySqlGeneratorFactory, SqliteQuerySqlGeneratorFactory>()
  21. .TryAdd<IQueryableMethodTranslatingExpressionVisitorFactory, SqliteQueryableMethodTranslatingExpressionVisitorFactory>()
  22. .TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, SqliteSqlTranslatingExpressionVisitorFactory>()
  23. .TryAdd<IQueryTranslationPostprocessorFactory, SqliteQueryTranslationPostprocessorFactory>()
  24. .TryAdd<IUpdateSqlGenerator>(
  25. sp =>
  26. {
  27. // Support for the RETURNING clause on INSERT/UPDATE/DELETE was added in Sqlite 3.35.
  28. // Detect which version we're using, and fall back to the older INSERT/UPDATE+SELECT behavior on legacy versions.
  29. var dependencies = sp.GetRequiredService<UpdateSqlGeneratorDependencies>();
  30. return new Version(new SqliteConnection().ServerVersion) < new Version(3, 35)
  31. ? new SqliteLegacyUpdateSqlGenerator(dependencies)
  32. : new SqliteUpdateSqlGenerator(dependencies);
  33. })
  34. .TryAdd<ISqlExpressionFactory, SqliteSqlExpressionFactory>()
  35. .TryAddProviderSpecificServices(
  36. b => b.TryAddScoped<ISqliteRelationalConnection, SqliteRelationalConnection>());
  37. builder.TryAddCoreServices();
  38. return serviceCollection;
  39. }

 

  1. using EfDemo.Models;
  2. using Microsoft.EntityFrameworkCore.Infrastructure;
  3. using Microsoft.EntityFrameworkCore.Query.Internal;
  4. using Microsoft.EntityFrameworkCore.Query;
  5. using Microsoft.EntityFrameworkCore;
  6. using EfDemo.QueryableExtension;
  7. using EfDemo.DBExtension;
  8. using Microsoft.EntityFrameworkCore.Diagnostics;
  9. namespace EfDemo.DB
  10. {
  11. public partial class DecodeMicroMsgContext : DbContext
  12. {
  13. public DecodeMicroMsgContext(Func<IServiceCollection> func, IServiceProvider serviceProvider)
  14. {
  15. Func = func;
  16. ServiceProvider = serviceProvider;
  17. }
  18. public DecodeMicroMsgContext(DbContextOptions<DecodeMicroMsgContext> options, Func<IServiceCollection> func,IServiceProvider serviceProvider)
  19. : base(options)
  20. {
  21. Func = func;
  22. ServiceProvider = serviceProvider;
  23. }
  24. public virtual DbSet<AppInfo> AppInfos { get; set; }
  25. public virtual DbSet<BizInfo> BizInfos { get; set; }
  26. public virtual DbSet<BizName2Id> BizName2Ids { get; set; }
  27. public virtual DbSet<BizProfileInfo> BizProfileInfos { get; set; }
  28. public virtual DbSet<BizProfileV2> BizProfileV2s { get; set; }
  29. public virtual DbSet<BizSessionNewFeed> BizSessionNewFeeds { get; set; }
  30. public virtual DbSet<ChatInfo> ChatInfos { get; set; }
  31. public virtual DbSet<ChatLiveInfo> ChatLiveInfos { get; set; }
  32. public virtual DbSet<ChatRoom> ChatRooms { get; set; }
  33. public virtual DbSet<ChatRoomInfo> ChatRoomInfos { get; set; }
  34. public virtual DbSet<ChatroomTool> ChatroomTools { get; set; }
  35. public virtual DbSet<Contact> Contacts { get; set; }
  36. public virtual DbSet<ContactHeadImgUrl> ContactHeadImgUrls { get; set; }
  37. public virtual DbSet<ContactLabel> ContactLabels { get; set; }
  38. public virtual DbSet<DelayDownLoad> DelayDownLoads { get; set; }
  39. public virtual DbSet<FtschatroomTran> FtschatroomTrans { get; set; }
  40. public virtual DbSet<FtscontactTran> FtscontactTrans { get; set; }
  41. public virtual DbSet<MainConfig> MainConfigs { get; set; }
  42. public virtual DbSet<OpLog> OpLogs { get; set; }
  43. public virtual DbSet<PatInfo> PatInfos { get; set; }
  44. public virtual DbSet<RevokeMsgStorage> RevokeMsgStorages { get; set; }
  45. public virtual DbSet<Session> Sessions { get; set; }
  46. public virtual DbSet<TicketInfo> TicketInfos { get; set; }
  47. public virtual DbSet<TopStoryReddotInfo> TopStoryReddotInfos { get; set; }
  48. public IServiceProvider ServiceProvider { get; }
  49. public IServiceCollection ServiceDescriptors { get; }
  50. public Func<IServiceCollection> Func { get; }
  51. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  52. {
  53. optionsBuilder.UseInternalServiceProvider(ServiceProvider);
  54. // optionsBuilder.ReplaceService<IAsyncQueryProvider, TestQueryProvider>();
  55. // optionsBuilder.ReplaceService<IQueryCompiler, TestQueryCompiler>();
  56. // ((IDbContextOptionsBuilderInfrastructure)s).AddOrUpdateExtension(new Extension());
  57. //var interceptor = new List<IInterceptor>();
  58. //optionsBuilder.AddInterceptors();
  59. ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(new ServiceExtension(Func));
  60. base.OnConfiguring(optionsBuilder);
  61. }
  62. protected override void OnModelCreating(ModelBuilder modelBuilder)
  63. {
  64. modelBuilder.Entity<AppInfo>(entity =>
  65. {
  66. entity.HasKey(e => e.InfoKey);
  67. entity.ToTable("AppInfo");
  68. entity.Property(e => e.Description4EnUs).HasColumnName("Description4EnUS");
  69. entity.Property(e => e.Description4ZhTw).HasColumnName("Description4ZhTW");
  70. entity.Property(e => e.Name4EnUs).HasColumnName("Name4EnUS");
  71. entity.Property(e => e.Name4ZhTw).HasColumnName("Name4ZhTW");
  72. entity.Property(e => e.Version).HasColumnType("INT");
  73. });
  74. modelBuilder.Entity<BizInfo>(entity =>
  75. {
  76. entity.HasKey(e => e.UserName);
  77. entity.ToTable("BizInfo");
  78. entity.Property(e => e.AcceptType).HasDefaultValueSql("0");
  79. entity.Property(e => e.BrandFlag).HasDefaultValueSql("0");
  80. entity.Property(e => e.BrandIconUrl).HasColumnName("BrandIconURL");
  81. entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
  82. entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
  83. entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
  84. entity.Property(e => e.Reserved7).HasDefaultValueSql("0");
  85. entity.Property(e => e.Type).HasDefaultValueSql("0");
  86. entity.Property(e => e.UpdateTime).HasDefaultValueSql("0");
  87. });
  88. modelBuilder.Entity<BizName2Id>(entity =>
  89. {
  90. entity.HasKey(e => e.UsrName);
  91. entity.ToTable("BizName2ID");
  92. });
  93. modelBuilder.Entity<BizProfileInfo>(entity =>
  94. {
  95. entity.HasKey(e => e.TableIndex);
  96. entity.ToTable("BizProfileInfo");
  97. entity.HasIndex(e => e.TableIndex, "versionIdx");
  98. entity.Property(e => e.TableIndex)
  99. .ValueGeneratedNever()
  100. .HasColumnName("tableIndex");
  101. entity.Property(e => e.TableDesc).HasColumnName("tableDesc");
  102. entity.Property(e => e.TableVersion)
  103. .HasColumnType("INTERGER")
  104. .HasColumnName("tableVersion");
  105. });
  106. modelBuilder.Entity<BizProfileV2>(entity =>
  107. {
  108. entity.HasKey(e => e.TalkerId);
  109. entity.ToTable("BizProfileV2");
  110. entity.HasIndex(e => e.TimeStamp, "BizProfileV2TimeIdx");
  111. entity.Property(e => e.TalkerId).ValueGeneratedNever();
  112. });
  113. modelBuilder.Entity<BizSessionNewFeed>(entity =>
  114. {
  115. entity.HasKey(e => e.TalkerId);
  116. entity.HasIndex(e => e.CreateTime, "BizSessionNewFeedsCreateTimeIdx");
  117. entity.HasIndex(e => e.UpdateTime, "BizSessionNewFeedsUpdateTimeIdx");
  118. entity.Property(e => e.TalkerId).ValueGeneratedNever();
  119. });
  120. modelBuilder.Entity<ChatInfo>(entity =>
  121. {
  122. entity
  123. .HasNoKey()
  124. .ToTable("ChatInfo");
  125. entity.HasIndex(e => e.Username, "ChatInfoUserNameIndex");
  126. });
  127. modelBuilder.Entity<ChatLiveInfo>(entity =>
  128. {
  129. entity
  130. .HasNoKey()
  131. .ToTable("ChatLiveInfo");
  132. entity.HasIndex(e => new { e.RoomName, e.LiveId }, "IX_ChatLiveInfo_RoomName_LiveId").IsUnique();
  133. entity.HasIndex(e => e.LiveId, "ChatLiveInfoLiveIdIdx");
  134. entity.HasIndex(e => e.RoomName, "ChatLiveInfoRoomNamex");
  135. });
  136. modelBuilder.Entity<ChatRoom>(entity =>
  137. {
  138. entity.HasKey(e => e.ChatRoomName);
  139. entity.ToTable("ChatRoom");
  140. entity.Property(e => e.ChatRoomFlag)
  141. .HasDefaultValueSql("0")
  142. .HasColumnType("INT");
  143. entity.Property(e => e.IsShowName).HasDefaultValueSql("0");
  144. entity.Property(e => e.Owner).HasDefaultValueSql("0");
  145. entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
  146. entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
  147. entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
  148. entity.Property(e => e.Reserved7).HasDefaultValueSql("0");
  149. });
  150. modelBuilder.Entity<ChatRoomInfo>(entity =>
  151. {
  152. entity.HasKey(e => e.ChatRoomName);
  153. entity.ToTable("ChatRoomInfo");
  154. entity.Property(e => e.AnnouncementPublishTime).HasDefaultValueSql("0");
  155. entity.Property(e => e.ChatRoomStatus).HasDefaultValueSql("0");
  156. entity.Property(e => e.InfoVersion).HasDefaultValueSql("0");
  157. entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
  158. entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
  159. entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
  160. entity.Property(e => e.Reserved7).HasDefaultValueSql("0");
  161. });
  162. modelBuilder.Entity<ChatroomTool>(entity =>
  163. {
  164. entity
  165. .HasNoKey()
  166. .ToTable("ChatroomTool");
  167. entity.HasIndex(e => e.ChatroomUsername, "IX_ChatroomTool_ChatroomUsername").IsUnique();
  168. });
  169. modelBuilder.Entity<Contact>(entity =>
  170. {
  171. entity.HasKey(e => e.UserName);
  172. entity.ToTable("Contact");
  173. entity.HasIndex(e => e.Reserved2, "Contact_Idx0");
  174. entity.Property(e => e.ChatRoomNotify).HasDefaultValueSql("0");
  175. entity.Property(e => e.ChatRoomType).HasColumnType("INT");
  176. entity.Property(e => e.DelFlag).HasDefaultValueSql("0");
  177. entity.Property(e => e.LabelIdlist).HasColumnName("LabelIDList");
  178. entity.Property(e => e.Pyinitial).HasColumnName("PYInitial");
  179. entity.Property(e => e.RemarkPyinitial).HasColumnName("RemarkPYInitial");
  180. entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
  181. entity.Property(e => e.Reserved2).HasDefaultValueSql("0");
  182. entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
  183. entity.Property(e => e.Reserved8).HasDefaultValueSql("0");
  184. entity.Property(e => e.Reserved9).HasDefaultValueSql("0");
  185. entity.Property(e => e.Type).HasDefaultValueSql("0");
  186. entity.Property(e => e.VerifyFlag).HasDefaultValueSql("0");
  187. });
  188. modelBuilder.Entity<ContactHeadImgUrl>(entity =>
  189. {
  190. entity.HasKey(e => e.UsrName);
  191. entity.ToTable("ContactHeadImgUrl");
  192. entity.HasIndex(e => e.Reverse0, "reverse0Index");
  193. entity.Property(e => e.UsrName).HasColumnName("usrName");
  194. entity.Property(e => e.BigHeadImgUrl).HasColumnName("bigHeadImgUrl");
  195. entity.Property(e => e.HeadImgMd5).HasColumnName("headImgMd5");
  196. entity.Property(e => e.Reverse0)
  197. .HasColumnType("INT")
  198. .HasColumnName("reverse0");
  199. entity.Property(e => e.Reverse1).HasColumnName("reverse1");
  200. entity.Property(e => e.SmallHeadImgUrl).HasColumnName("smallHeadImgUrl");
  201. });
  202. modelBuilder.Entity<ContactLabel>(entity =>
  203. {
  204. entity.HasKey(e => e.LabelId);
  205. entity.ToTable("ContactLabel");
  206. entity.Property(e => e.LabelId).ValueGeneratedNever();
  207. });
  208. modelBuilder.Entity<DelayDownLoad>(entity =>
  209. {
  210. entity
  211. .HasNoKey()
  212. .ToTable("DelayDownLoad");
  213. entity.HasIndex(e => e.MessageServId, "IX_DelayDownLoad_MessageServId").IsUnique();
  214. });
  215. modelBuilder.Entity<FtschatroomTran>(entity =>
  216. {
  217. entity
  218. .HasNoKey()
  219. .ToTable("FTSChatroomTrans");
  220. entity.Property(e => e.DisplayName).HasColumnName("displayName");
  221. entity.Property(e => e.GroupUsername).HasColumnName("groupUsername");
  222. entity.Property(e => e.Nickname).HasColumnName("nickname");
  223. entity.Property(e => e.Operation).HasColumnName("operation");
  224. entity.Property(e => e.Reserve1).HasColumnName("reserve1");
  225. entity.Property(e => e.Reserve2).HasColumnName("reserve2");
  226. entity.Property(e => e.Username).HasColumnName("username");
  227. });
  228. modelBuilder.Entity<FtscontactTran>(entity =>
  229. {
  230. entity
  231. .HasNoKey()
  232. .ToTable("FTSContactTrans");
  233. entity.Property(e => e.Reserve1).HasColumnName("reserve1");
  234. entity.Property(e => e.Reserve2).HasColumnName("reserve2");
  235. entity.Property(e => e.Username).HasColumnName("username");
  236. });
  237. modelBuilder.Entity<MainConfig>(entity =>
  238. {
  239. entity.HasKey(e => e.Key);
  240. entity.ToTable("MainConfig");
  241. entity.HasIndex(e => e.Reserved0, "MainConfigReserved0Idx");
  242. entity.HasIndex(e => e.Reserved1, "MainConfigReserved1Idx");
  243. entity.Property(e => e.Reserved0).HasColumnType("INT");
  244. entity.Property(e => e.Reserved1).HasColumnType("INT");
  245. });
  246. modelBuilder.Entity<OpLog>(entity =>
  247. {
  248. entity.ToTable("OpLog");
  249. entity.Property(e => e.Id)
  250. .ValueGeneratedNever()
  251. .HasColumnName("ID");
  252. entity.Property(e => e.CmditemBuffer).HasColumnName("CMDItemBuffer");
  253. });
  254. modelBuilder.Entity<PatInfo>(entity =>
  255. {
  256. entity.HasKey(e => e.Username);
  257. entity.ToTable("PatInfo");
  258. entity.Property(e => e.Username).HasColumnName("username");
  259. entity.Property(e => e.Reserved1)
  260. .HasDefaultValueSql("0")
  261. .HasColumnName("reserved1");
  262. entity.Property(e => e.Reserved2)
  263. .HasDefaultValueSql("0")
  264. .HasColumnName("reserved2");
  265. entity.Property(e => e.Reserved3)
  266. .HasDefaultValueSql("0")
  267. .HasColumnName("reserved3");
  268. entity.Property(e => e.Reserved4)
  269. .HasDefaultValueSql("0")
  270. .HasColumnName("reserved4");
  271. entity.Property(e => e.Reserved5).HasColumnName("reserved5");
  272. entity.Property(e => e.Reserved6).HasColumnName("reserved6");
  273. entity.Property(e => e.Reserved7).HasColumnName("reserved7");
  274. entity.Property(e => e.Reserved8).HasColumnName("reserved8");
  275. entity.Property(e => e.Reserved9).HasColumnName("reserved9");
  276. entity.Property(e => e.Suffix).HasColumnName("suffix");
  277. });
  278. modelBuilder.Entity<RevokeMsgStorage>(entity =>
  279. {
  280. entity.HasKey(e => e.CreateTime);
  281. entity.ToTable("RevokeMsgStorage");
  282. entity.HasIndex(e => e.MsgSvrId, "MsgSvrId_Idx");
  283. entity.HasIndex(e => e.RevokeSvrId, "RevokeSvrID_Idx");
  284. entity.Property(e => e.CreateTime).ValueGeneratedNever();
  285. entity.Property(e => e.MsgSvrId)
  286. .HasColumnType("INTERGER")
  287. .HasColumnName("MsgSvrID");
  288. entity.Property(e => e.RevokeSvrId)
  289. .HasColumnType("INTERGER")
  290. .HasColumnName("RevokeSvrID");
  291. });
  292. modelBuilder.Entity<Session>(entity =>
  293. {
  294. entity.HasKey(e => e.StrUsrName);
  295. entity.ToTable("Session");
  296. entity.HasIndex(e => e.NOrder, "nOrderIndex");
  297. entity.Property(e => e.StrUsrName).HasColumnName("strUsrName");
  298. entity.Property(e => e.BytesXml).HasColumnName("bytesXml");
  299. entity.Property(e => e.EditContent).HasColumnName("editContent");
  300. entity.Property(e => e.NIsSend).HasColumnName("nIsSend");
  301. entity.Property(e => e.NMsgLocalId).HasColumnName("nMsgLocalID");
  302. entity.Property(e => e.NMsgStatus).HasColumnName("nMsgStatus");
  303. entity.Property(e => e.NMsgType).HasColumnName("nMsgType");
  304. entity.Property(e => e.NOrder)
  305. .HasDefaultValueSql("0")
  306. .HasColumnType("INT")
  307. .HasColumnName("nOrder");
  308. entity.Property(e => e.NStatus).HasColumnName("nStatus");
  309. entity.Property(e => e.NTime).HasColumnName("nTime");
  310. entity.Property(e => e.NUnReadCount)
  311. .HasDefaultValueSql("0")
  312. .HasColumnName("nUnReadCount");
  313. entity.Property(e => e.OthersAtMe)
  314. .HasColumnType("INT")
  315. .HasColumnName("othersAtMe");
  316. entity.Property(e => e.ParentRef).HasColumnName("parentRef");
  317. entity.Property(e => e.Reserved0).HasDefaultValueSql("0");
  318. entity.Property(e => e.Reserved2).HasDefaultValueSql("0");
  319. entity.Property(e => e.Reserved4).HasDefaultValueSql("0");
  320. entity.Property(e => e.StrContent).HasColumnName("strContent");
  321. entity.Property(e => e.StrNickName).HasColumnName("strNickName");
  322. });
  323. modelBuilder.Entity<TicketInfo>(entity =>
  324. {
  325. entity.HasKey(e => e.UserName);
  326. entity.ToTable("TicketInfo");
  327. entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
  328. entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
  329. });
  330. modelBuilder.Entity<TopStoryReddotInfo>(entity =>
  331. {
  332. entity.HasKey(e => e.MsgId);
  333. entity.ToTable("TopStoryReddotInfo");
  334. entity.Property(e => e.H5version).HasColumnName("H5Version");
  335. });
  336. OnModelCreatingPartial(modelBuilder);
  337. }
  338. partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
  339. }
  340. }

结尾

    在本文中,我们一共讲了AddDbContext做了什么,DBContext的构造函数又做了那些事情,在写了不托管EF的ioc到WEB的ioc的场景下如果注入服务到EF的ioc中,以及如何拦截增删改查的方式,提升查询性能的方式,以及最后的EF的ioc托管到WEB的ioc,本文作为源码讲解的第一章,觉得写的有点多,如果又看不懂的地方,或者代码下载下来没办法运行或者保存的地方可以随时联系我,QQ934550201.我们下次再见。数据库是我破解的我本地的微信数据的一部分,emmm作为了本次的例子,我希望大家能够合理看待我的这个数据库的数据,不要做一些不好的事情,谢谢大家。

    本次的代码例子地址

               链接:https://pan.baidu.com/s/1w6kFG5MCJYE0jzEBxjQTKw

               提取码:foin

    之前AOP的代码例子

    链接:https://pan.baidu.com/s/1AJe4-KhjIESbDtFNqM968Q
    提取码:jp20

原文链接:https://www.cnblogs.com/1996-Chinese-Chen/p/17761733.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号