• 【源码解读(一)】EFCORE源码解读之创建DBContext查询拦截


    引言

        在网上很少看到有关于系统讲解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。

    复制代码
     public static IServiceCollection AddDbContext(
         this IServiceCollection serviceCollection,
         Action? optionsAction,
         ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
         ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
         where TContextImplementation : DbContext, TContextService
     {
         if (contextLifetime == ServiceLifetime.Singleton)
         {
             optionsLifetime = ServiceLifetime.Singleton;
         }
    
         if (optionsAction != null)
         {
             CheckContextConstructors();
         }
    
         AddCoreServices(serviceCollection, optionsAction, optionsLifetime);
    
         if (serviceCollection.Any(d => d.ServiceType == typeof(IDbContextFactorySource)))
         {
             // Override registration made by AddDbContextFactory
             var serviceDescriptor = serviceCollection.FirstOrDefault(d => d.ServiceType == typeof(TContextImplementation));
             if (serviceDescriptor != null)
             {
                 serviceCollection.Remove(serviceDescriptor);
             }
         }
    
         serviceCollection.TryAdd(new ServiceDescriptor(typeof(TContextService), typeof(TContextImplementation), contextLifetime));
    
         if (typeof(TContextService) != typeof(TContextImplementation))
         {
             serviceCollection.TryAdd(
                 new ServiceDescriptor(
                     typeof(TContextImplementation),
                     p => (TContextImplementation)p.GetService()!,
                     contextLifetime));
         }
    
         return serviceCollection;
     }

    复制代码
    复制代码
     private static void CheckContextConstructors()
         where TContext : DbContext
     {
         var declaredConstructors = typeof(TContext).GetTypeInfo().DeclaredConstructors.ToList();
         if (declaredConstructors.Count == 1
             && declaredConstructors[0].GetParameters().Length == 0)
         {
             throw new ArgumentException(CoreStrings.DbContextMissingConstructor(typeof(TContext).ShortDisplayName()));
         }
     }
    复制代码

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

    复制代码
        private static void AddCoreServices(
            IServiceCollection serviceCollection,
            Action? optionsAction,
            ServiceLifetime optionsLifetime)
            where TContextImplementation : DbContext
        {
            serviceCollection.TryAdd(
                new ServiceDescriptor(
                    typeof(DbContextOptions),
                    p => CreateDbContextOptions(p, optionsAction),
                    optionsLifetime));
    
            serviceCollection.Add(
                new ServiceDescriptor(
                    typeof(DbContextOptions),
                    p => p.GetRequiredService>(),
                    optionsLifetime));
        }
    
        private static DbContextOptions CreateDbContextOptions(
            IServiceProvider applicationServiceProvider,
            Action? optionsAction)
            where TContext : DbContext
        {
            var builder = new DbContextOptionsBuilder(
                new DbContextOptions(new Dictionary()));
    
            builder.UseApplicationServiceProvider(applicationServiceProvider);
    
            optionsAction?.Invoke(applicationServiceProvider, builder);
    
            return builder.Options;
        }
    复制代码

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

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

    复制代码
     public new virtual DbContextOptionsBuilder UseApplicationServiceProvider(IServiceProvider? serviceProvider)
         => (DbContextOptionsBuilder)base.UseApplicationServiceProvider(serviceProvider);
      public virtual DbContextOptionsBuilder UseApplicationServiceProvider(IServiceProvider? serviceProvider)
          => WithOption(e => e.WithApplicationServiceProvider(serviceProvider));
        public virtual CoreOptionsExtension WithApplicationServiceProvider(IServiceProvider? applicationServiceProvider)
        {
            var clone = Clone();
    
            clone._applicationServiceProvider = applicationServiceProvider;
    
            return clone;
        }
    复制代码
    复制代码
      private DbContextOptionsBuilder WithOption(Func withFunc)
      {
          ((IDbContextOptionsBuilderInfrastructure)this).AddOrUpdateExtension(
              withFunc(Options.FindExtension() ?? new CoreOptionsExtension()));
    
          return this;
      }
    复制代码
    复制代码
    public interface IDbContextOptionsExtension
    {
        /// 
        ///     Information/metadata about the extension.
        /// 
        DbContextOptionsExtensionInfo Info { get; }
    
        /// 
        ///     Adds the services required to make the selected options work. This is used when there
        ///     is no external  and EF is maintaining its own service
        ///     provider internally. This allows database providers (and other extensions) to register their
        ///     required services when EF is creating an service provider.
        /// 
        /// The collection to add services to.
        void ApplyServices(IServiceCollection services);
    
        /// 
        ///     Gives the extension a chance to validate that all options in the extension are valid.
        ///     Most extensions do not have invalid combinations and so this will be a no-op.
        ///     If options are invalid, then an exception should be thrown.
        /// 
        /// The options being validated.
        void Validate(IDbContextOptions options);
    }
    复制代码
        void IDbContextOptionsBuilderInfrastructure.AddOrUpdateExtension(TExtension extension)
            => _options = _options.WithExtension(extension);

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

    复制代码
    if (serviceCollection.Any(d => d.ServiceType == typeof(IDbContextFactorySource)))
    {
        // Override registration made by AddDbContextFactory
        var serviceDescriptor = serviceCollection.FirstOrDefault(d => d.ServiceType == typeof(TContextImplementation));
        if (serviceDescriptor != null)
        {
            serviceCollection.Remove(serviceDescriptor);
        }
    }
    
    serviceCollection.TryAdd(new ServiceDescriptor(typeof(TContextService), typeof(TContextImplementation), contextLifetime));
    
    if (typeof(TContextService) != typeof(TContextImplementation))
    {
        serviceCollection.TryAdd(
            new ServiceDescriptor(
                typeof(TContextImplementation),
                p => (TContextImplementation)p.GetService()!,
                contextLifetime));
    }
    复制代码

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

    DBContext构造

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

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

        下面是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。

    复制代码
    public class ServiceProviderCache
    {
        private readonly ConcurrentDictionarystring, string> DebugInfo)>
            _configurations = new();
    
        /// 
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// 
        public static ServiceProviderCache Instance { get; } = new();
    
        /// 
        ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
        ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
        ///     any release. You should only use it directly in your code with extreme caution and knowing that
        ///     doing so can result in application failures when updating to a new Entity Framework Core release.
        /// 
        public virtual IServiceProvider GetOrAdd(IDbContextOptions options, bool providerRequired)
        {
            var coreOptionsExtension = options.FindExtension();
            var internalServiceProvider = coreOptionsExtension?.InternalServiceProvider;
            if (internalServiceProvider != null)
            {
                ValidateOptions(options);
    
                var optionsInitializer = internalServiceProvider.GetService();
                if (optionsInitializer == null)
                {
                    throw new InvalidOperationException(CoreStrings.NoEfServices);
                }
    
                if (providerRequired)
                {
                    optionsInitializer.EnsureInitialized(internalServiceProvider, options);
                }
    
                return internalServiceProvider;
            }
    
            if (coreOptionsExtension?.ServiceProviderCachingEnabled == false)
            {
                return BuildServiceProvider(options, (_configurations, options)).ServiceProvider;
            }
    
            var cacheKey = options;
            var extension = options.FindExtension();
            if (extension?.ApplicationServiceProvider != null)
            {
                cacheKey = ((DbContextOptions)options).WithExtension(extension.WithApplicationServiceProvider(null));
            }
    
            return _configurations.GetOrAdd(
                    cacheKey,
                    static (contextOptions, tuples) => BuildServiceProvider(contextOptions, tuples), (_configurations, options))
                .ServiceProvider;
    
            static (IServiceProvider ServiceProvider, IDictionary<string, string> DebugInfo) BuildServiceProvider(
                IDbContextOptions _,
                (ConcurrentDictionarystring, string> DebugInfo)>,
                    IDbContextOptions) arguments)
            {
                var (configurations, options) = arguments;
    
                ValidateOptions(options);
    
                var debugInfo = new Dictionary<string, string>();
                foreach (var optionsExtension in options.Extensions)
                {
                    optionsExtension.Info.PopulateDebugInfo(debugInfo);
                }
    
                debugInfo = debugInfo.OrderBy(_ => debugInfo.Keys).ToDictionary(d => d.Key, v => v.Value);
    
                var services = new ServiceCollection();
                var hasProvider = ApplyServices(options, services);
    
                var replacedServices = options.FindExtension()?.ReplacedServices;
                if (replacedServices != null)
                {
                    var updatedServices = new ServiceCollection();
                    foreach (var descriptor in services)
                    {
                        if (replacedServices.TryGetValue((descriptor.ServiceType, descriptor.ImplementationType), out var replacementType))
                        {
                            ((IList)updatedServices).Add(
                                new ServiceDescriptor(descriptor.ServiceType, replacementType, descriptor.Lifetime));
                        }
                        else if (replacedServices.TryGetValue((descriptor.ServiceType, null), out replacementType))
                        {
                            ((IList)updatedServices).Add(
                                new ServiceDescriptor(descriptor.ServiceType, replacementType, descriptor.Lifetime));
                        }
                        else
                        {
                            ((IList)updatedServices).Add(descriptor);
                        }
                    }
    
                    services = updatedServices;
                }
    
                var serviceProvider = services.BuildServiceProvider();
    
                if (hasProvider)
                {
                    serviceProvider
                        .GetRequiredService()
                        .EnsureInitialized(serviceProvider, options);
                }
    
                using (var scope = serviceProvider.CreateScope())
                {
                    var scopedProvider = scope.ServiceProvider;
    
                    // If loggingDefinitions is null, then there is no provider yet
                    var loggingDefinitions = scopedProvider.GetService();
                    if (loggingDefinitions != null)
                    {
                        // Because IDbContextOptions cannot yet be resolved from the internal provider
                        var logger = new DiagnosticsLogger(
                            ScopedLoggerFactory.Create(scopedProvider, options),
                            scopedProvider.GetRequiredService(),
                            scopedProvider.GetRequiredService(),
                            loggingDefinitions,
                            new NullDbContextLogger());
    
                        if (configurations.IsEmpty)
                        {
                            logger.ServiceProviderCreated(serviceProvider);
                        }
                        else
                        {
                            logger.ServiceProviderDebugInfo(
                                debugInfo,
                                configurations.Values.Select(v => v.DebugInfo).ToList());
    
                            if (configurations.Count >= 20)
                            {
                                logger.ManyServiceProvidersCreatedWarning(
                                    configurations.Values.Select(e => e.ServiceProvider).ToList());
                            }
                        }
    
                        var applicationServiceProvider = options.FindExtension()?.ApplicationServiceProvider;
                        if (applicationServiceProvider?.GetService() != null)
                        {
                            logger.RedundantAddServicesCallWarning(serviceProvider);
                        }
                    }
                }
    
                return (serviceProvider, debugInfo);
            }
        }
    
        private static void ValidateOptions(IDbContextOptions options)
        {
            foreach (var extension in options.Extensions)
            {
                extension.Validate(options);
            }
        }
    
        private static bool ApplyServices(IDbContextOptions options, ServiceCollection services)
        {
            var coreServicesAdded = false;
    
            foreach (var extension in options.Extensions)
            {
                extension.ApplyServices(services);
    
                if (extension.Info.IsDatabaseProvider)
                {
                    coreServicesAdded = true;
                }
            }
    
            if (coreServicesAdded)
            {
                return true;
            }
    
            new EntityFrameworkServicesBuilder(services).TryAddCoreServices();
    
            return false;
        }
    }
    复制代码

         我们先看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,在后面的代码就是一些日志相关的配置,此处就不过多讲解。

        public virtual void ApplyServices(IServiceCollection services)
            => 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注入的服务。

        builder.Services.AddScoped();
        builder.Services.AddSingleton(() => builder.Services);
      [InjectAttribute]
      public interface IWebGetName
      {
          public string GetName();
      }
    复制代码
     [AttributeUsage(AttributeTargets.Interface| AttributeTargets.Class)]
     public class InjectAttribute:Attribute
     {
         public InjectAttribute()
         {
                 
         }
     }
    复制代码

     

    复制代码
    public class ServiceExtension : IDbContextOptionsExtension
    {
        public ServiceExtension(Func func)
        {
            Func = func;
        }
        public DbContextOptionsExtensionInfo Info => new ExtensionInfo(this);
    
        public Func Func { get; }
    
        public void ApplyServices(IServiceCollection services)
        {
            var ser=Func();
            var type = ser.Where(s => s.ServiceType?.GetCustomAttribute() != null || s.ImplementationType?.GetCustomAttribute() != null).ToList();
            foreach (var item in type)
            {
                services.TryAdd(new ServiceDescriptor(item.ServiceType, item.ImplementationType, item.Lifetime));
            }
            services.AddScoped();
        }
    
        public void Validate(IDbContextOptions options)
        {
        }
    }
     public class ExtensionInfo: DbContextOptionsExtensionInfo
     {
         public ExtensionInfo(IDbContextOptionsExtension extension):base(extension) 
         {
                 
         }
         public override bool IsDatabaseProvider => false;
    
         public override string LogFragment => string.Empty;
    
         public override int GetServiceProviderHashCode()
         {
             return 0;
         }
    
         public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
         {
         }
    
         public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
         {
             return true;
         }
     }
    复制代码

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

    复制代码
      builder.Services.AddDbContext((a, m) => {
    
          ((IDbContextOptionsBuilderInfrastructure)m).AddOrUpdateExtension(new ServiceExtension(a.GetService>()));
          m.UseSqlite("Data Source=C:\\Users\\Chenxd\\Desktop\\资料\\CrackMsg\\CrackDb\\CrackDb\\bin\\Debug\\net7.0-windows\\TOOLS\\output\\decode_MicroMsg.db;");
      });
    复制代码

    拦截查询

      Sql拦截

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

        var list=new List();
        list.Add(new DbContextInterceptor());
        optionsBuilder.AddInterceptors(list);
    复制代码
        public class DbContextInterceptor:DbCommandInterceptor
        {
            private  DbConnection _connection;
            private  DbCommand _command;
            private CommandSource _commandSource;
            public DbContextInterceptor()
            {
    
            }
            public override DbCommand CommandCreated(CommandEndEventData eventData, DbCommand result)
            {
                return _command;
            }
            public override InterceptionResult CommandCreating(CommandCorrelatedEventData eventData, InterceptionResult result)
            {
                _commandSource = eventData.CommandSource;
                if (eventData.CommandSource==CommandSource.LinqQuery)
                {
                    _connection = new MySqlConnection(eventData.Connection.ConnectionString);
                    _command = new MySqlCommand();
                }
                return InterceptionResult.SuppressWithResult(_command);
            }
            public override void CommandFailed(DbCommand command, CommandErrorEventData eventData)
            {
                base.CommandFailed(command, eventData);
            }
            public override Task CommandFailedAsync(DbCommand command, CommandErrorEventData eventData, CancellationToken cancellationToken = default)
            {
                return base.CommandFailedAsync(command, eventData, cancellationToken);
            }
            public override InterceptionResult DataReaderDisposing(DbCommand command, DataReaderDisposingEventData eventData, InterceptionResult result)
            {
                return base.DataReaderDisposing(command, eventData, result);
            }
            public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
            {
                return base.NonQueryExecuted(command, eventData, result);
            }
            public override ValueTask<int> NonQueryExecutedAsync(DbCommand command, CommandExecutedEventData eventData, int result, CancellationToken cancellationToken = default)
            {
                return base.NonQueryExecutedAsync(command, eventData, result, cancellationToken);
            }
            public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
            {
                return base.NonQueryExecuting(command, eventData, result);
            }
            public override ValueTaskint>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
            {
                return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
            }
            public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
            {
                return base.ReaderExecuted(command, eventData, result);
            }
            public override ValueTask ReaderExecutedAsync(DbCommand command, CommandExecutedEventData eventData, DbDataReader result, CancellationToken cancellationToken = default)
            {
                return base.ReaderExecutedAsync(command, eventData, result, cancellationToken);
            }
            public override InterceptionResult ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result)
            {
                command.CommandText = "";
                return base.ReaderExecuting(command, eventData, result);
            }
            public async override ValueTask> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)
            {
                InterceptionResult results;
                if (_commandSource == CommandSource.LinqQuery)
                {
                    var connect = new MySqlConnection("data source=192.168.21.129;database=MasterSlave; userid=root;pwd=199645; charset=utf8;ConvertZeroDateTime=True;pooling=true; allowuservariables=true;");
                    connect.Open();
                    _command = new MySqlCommand(command.CommandText, connect);
                    var reader = await _command.ExecuteReaderAsync();
                    results = InterceptionResult.SuppressWithResult(reader);
                }
                else
                {
                    results=await base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
                }
                return results;
            }
            public override object ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object result)
            {
                return base.ScalarExecuted(command, eventData, result);
            }
            public override ValueTask<object> ScalarExecutedAsync(DbCommand command, CommandExecutedEventData eventData, object result, CancellationToken cancellationToken = default)
            {
                return base.ScalarExecutedAsync(command, eventData, result, cancellationToken);
            }
            public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
            {
                return base.ScalarExecuting(command, eventData, result);
            }
            public override ValueTaskobject>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default)
            {
                return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
            }
        }
    复制代码

      表达式拦截

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

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

        IQueryCompiler

    复制代码
    public class TestQueryCompiler : IQueryCompiler
        {
            public TestQueryCompiler(IDatabase database, IQueryContextFactory queryContextFactory)
            {
                Database = database;
                var a = Database.GetType();
                QueryContextFactory = queryContextFactory;
            }
    
            public IDatabase Database { get; }
            public IQueryContextFactory QueryContextFactory { get; }
    
            public Func CreateCompiledAsyncQuery(Expression query)
            {
                var queryFunc = Database.CompileQuery(query, true);
                return queryFunc;
            }
    
            public Func CreateCompiledQuery(Expression query)
            {
                var queryFunc = Database.CompileQuery(query, false);
                return queryFunc;
            }
    
            public TResult Execute(Expression query)
            {
                var queryFunc = Database.CompileQuery(query, false);
                var res = queryFunc(QueryContextFactory.Create());
                return res;
            }
    
            public TResult ExecuteAsync(Expression query, CancellationToken cancellationToken)
            {
                var queryFunc = Database.CompileQuery(query, true);
                var res = queryFunc(QueryContextFactory.Create());
                return res;
            }
        }
    复制代码

        IAsyncQueryProvider

        

    复制代码
     public class TestQueryProvider : IAsyncQueryProvider
     {
         public TestQueryProvider(IDBGetName ta, IQueryCompiler query,IWebGetName webGetName)
         {
             Ta = ta;
             Query = query;
             WebGetName = webGetName;
         }
    
         public IDBGetName Ta { get; }
         public IQueryCompiler Query { get; }
         public IWebGetName WebGetName { get; }
    
         public IQueryable CreateQuery(Expression expression)
         {
             return new Queryable<object>(this, expression);
         }
    
         public IQueryable CreateQuery(Expression expression)
         {
             return new Queryable(this, expression);
         }
    
         public object? Execute(Expression expression)
         {
             return Query.Execute<object>(expression);
         }
    
         public TResult Execute(Expression expression)
         {
             return Query.Execute(expression);
         }
    
         public TResult ExecuteAsync(Expression expression, CancellationToken cancellationToken = default)
         {
             return Query.ExecuteAsync(expression, cancellationToken);
         }
     }  
    复制代码

        IQueryable

    复制代码
     public class Queryable : IQueryable
     {
         public Queryable(IAsyncQueryProvider queryProvider, Expression expression)
         {
             QueryProvider = queryProvider;
             Expression = expression;
         }
         public Type ElementType => typeof(T);
    
         public IAsyncQueryProvider QueryProvider { get; }
         public Expression Expression { get; }
    
         public IQueryProvider Provider => QueryProvider;
    
         public IEnumerator GetEnumerator()
         {
             return Provider.Execute(Expression).GetEnumerator();
         }
    
         IEnumerator IEnumerable.GetEnumerator()
         {
             return Provider.Execute>(Expression).GetEnumerator();
         }
     }
    复制代码

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

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

    复制代码
     #未接管
    optionsBuilder.ReplaceService(); optionsBuilder.ReplaceService();
    #接管IOC

       builder.Services.AddScoped();
       builder.Services.AddScoped(s =>
       {
       var database = s.GetService();
       var factory = s.GetService();
       return new TestQueryCompiler(database, factory);
       });

    复制代码

     

     

     缓存查询方法

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

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

    复制代码
      public class WeatherForecastController : ControllerBase
      {
          private static readonly string[] Summaries = new[]
          {
          "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
      };
          private Func> Func;
          private readonly ILogger _logger;
          private readonly IWebGetName webGetName;
    
          public WeatherForecastController(ILogger logger,IWebGetName webGetName, DecodeMicroMsgContext dbContext)
          {
              _logger = logger;
              this.webGetName = webGetName;
              DbContext = dbContext;
          }
    
          public DecodeMicroMsgContext DbContext { get; }
    
          [HttpGet(Name = "GetWeatherForecast")]
          public List Get()
          {
              Func = EF.CompileQuery>(s => s.Contacts.Take(10));
              return DbContext.Contacts.Take(10).ToList();
          }
    复制代码

    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方法的所有代码。

    复制代码
     public static void Main(string[] args)
     {
         var builder = WebApplication.CreateBuilder(args);
         builder.Services.AddScoped();
         builder.Services.AddScoped();
         builder.Services.AddScoped(s =>
         {
             var database = s.GetService();
             var factory = s.GetService();
             return new TestQueryCompiler(database, factory);
         });
         builder.Services.AddScoped();
         builder.Services.AddEntityFrameworkSqlite();
         builder.Services.AddDbContext((a, m) => {
             m.UseSqlite("Data Source=C:\\Users\\Chenxd\\Desktop\\资料\\CrackMsg\\CrackDb\\CrackDb\\bin\\Debug\\net7.0-windows\\TOOLS\\output\\decode_MicroMsg.db;");
         });
         builder.Services.AddControllers();
         // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
         builder.Services.AddEndpointsApiExplorer();
         builder.Services.AddSwaggerGen();
         builder.Services.AddSingleton(() => builder.Services);
         var app = builder.Build();
    
         // Configure the HTTP request pipeline.
         if (app.Environment.IsDevelopment())
         {
             app.UseSwagger();
             app.UseSwaggerUI();
         }
    
         app.UseAuthorization();
    
    
         app.MapControllers();
    
         app.Run();
     }
    复制代码

     

    复制代码
        public static IServiceCollection AddEntityFrameworkSqlite(this IServiceCollection serviceCollection)
        {
            var builder = new EntityFrameworkRelationalServicesBuilder(serviceCollection)
                .TryAdd()
                .TryAdd>()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd(p => p.GetRequiredService())
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd()
                .TryAdd(
                    sp =>
                    {
                        // Support for the RETURNING clause on INSERT/UPDATE/DELETE was added in Sqlite 3.35.
                        // Detect which version we're using, and fall back to the older INSERT/UPDATE+SELECT behavior on legacy versions.
                        var dependencies = sp.GetRequiredService();
    
                        return new Version(new SqliteConnection().ServerVersion) < new Version(3, 35)
                            ? new SqliteLegacyUpdateSqlGenerator(dependencies)
                            : new SqliteUpdateSqlGenerator(dependencies);
                    })
                .TryAdd()
                .TryAddProviderSpecificServices(
                    b => b.TryAddScoped());
    
            builder.TryAddCoreServices();
    
            return serviceCollection;
        }
    复制代码

     

    复制代码
    using EfDemo.Models;
    using Microsoft.EntityFrameworkCore.Infrastructure;
    using Microsoft.EntityFrameworkCore.Query.Internal;
    using Microsoft.EntityFrameworkCore.Query;
    using Microsoft.EntityFrameworkCore;
    using EfDemo.QueryableExtension;
    using EfDemo.DBExtension;
    using Microsoft.EntityFrameworkCore.Diagnostics;
    
    namespace EfDemo.DB
    {
    
        public partial class DecodeMicroMsgContext : DbContext
        {
            public DecodeMicroMsgContext(Func func, IServiceProvider serviceProvider)
            {
                Func = func;
                ServiceProvider = serviceProvider;
            }
    
            public DecodeMicroMsgContext(DbContextOptions options, Func func,IServiceProvider serviceProvider)
                : base(options)
            {
    
                Func = func;
                ServiceProvider = serviceProvider;
            }
    
            public virtual DbSet AppInfos { get; set; }
    
            public virtual DbSet BizInfos { get; set; }
    
            public virtual DbSet BizName2Ids { get; set; }
    
            public virtual DbSet BizProfileInfos { get; set; }
    
            public virtual DbSet BizProfileV2s { get; set; }
    
            public virtual DbSet BizSessionNewFeeds { get; set; }
    
            public virtual DbSet ChatInfos { get; set; }
    
            public virtual DbSet ChatLiveInfos { get; set; }
    
            public virtual DbSet ChatRooms { get; set; }
    
            public virtual DbSet ChatRoomInfos { get; set; }
    
            public virtual DbSet ChatroomTools { get; set; }
    
            public virtual DbSet Contacts { get; set; }
    
            public virtual DbSet ContactHeadImgUrls { get; set; }
    
            public virtual DbSet ContactLabels { get; set; }
    
            public virtual DbSet DelayDownLoads { get; set; }
    
            public virtual DbSet FtschatroomTrans { get; set; }
    
            public virtual DbSet FtscontactTrans { get; set; }
    
            public virtual DbSet MainConfigs { get; set; }
    
            public virtual DbSet OpLogs { get; set; }
    
            public virtual DbSet PatInfos { get; set; }
    
            public virtual DbSet RevokeMsgStorages { get; set; }
    
            public virtual DbSet Sessions { get; set; }
    
            public virtual DbSet TicketInfos { get; set; }
    
            public virtual DbSet TopStoryReddotInfos { get; set; }
            public IServiceProvider ServiceProvider { get; }
            public IServiceCollection ServiceDescriptors { get; }
            public Func Func { get; }
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseInternalServiceProvider(ServiceProvider);
               // optionsBuilder.ReplaceService();
               // optionsBuilder.ReplaceService();
                // ((IDbContextOptionsBuilderInfrastructure)s).AddOrUpdateExtension(new Extension());
                //var interceptor = new List();
                //optionsBuilder.AddInterceptors();
                ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(new ServiceExtension(Func));
                base.OnConfiguring(optionsBuilder);
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.InfoKey);
    
                    entity.ToTable("AppInfo");
    
                    entity.Property(e => e.Description4EnUs).HasColumnName("Description4EnUS");
                    entity.Property(e => e.Description4ZhTw).HasColumnName("Description4ZhTW");
                    entity.Property(e => e.Name4EnUs).HasColumnName("Name4EnUS");
                    entity.Property(e => e.Name4ZhTw).HasColumnName("Name4ZhTW");
                    entity.Property(e => e.Version).HasColumnType("INT");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.UserName);
    
                    entity.ToTable("BizInfo");
    
                    entity.Property(e => e.AcceptType).HasDefaultValueSql("0");
                    entity.Property(e => e.BrandFlag).HasDefaultValueSql("0");
                    entity.Property(e => e.BrandIconUrl).HasColumnName("BrandIconURL");
                    entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved7).HasDefaultValueSql("0");
                    entity.Property(e => e.Type).HasDefaultValueSql("0");
                    entity.Property(e => e.UpdateTime).HasDefaultValueSql("0");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.UsrName);
    
                    entity.ToTable("BizName2ID");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.TableIndex);
    
                    entity.ToTable("BizProfileInfo");
    
                    entity.HasIndex(e => e.TableIndex, "versionIdx");
    
                    entity.Property(e => e.TableIndex)
                        .ValueGeneratedNever()
                        .HasColumnName("tableIndex");
                    entity.Property(e => e.TableDesc).HasColumnName("tableDesc");
                    entity.Property(e => e.TableVersion)
                        .HasColumnType("INTERGER")
                        .HasColumnName("tableVersion");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.TalkerId);
    
                    entity.ToTable("BizProfileV2");
    
                    entity.HasIndex(e => e.TimeStamp, "BizProfileV2TimeIdx");
    
                    entity.Property(e => e.TalkerId).ValueGeneratedNever();
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.TalkerId);
    
                    entity.HasIndex(e => e.CreateTime, "BizSessionNewFeedsCreateTimeIdx");
    
                    entity.HasIndex(e => e.UpdateTime, "BizSessionNewFeedsUpdateTimeIdx");
    
                    entity.Property(e => e.TalkerId).ValueGeneratedNever();
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity
                        .HasNoKey()
                        .ToTable("ChatInfo");
    
                    entity.HasIndex(e => e.Username, "ChatInfoUserNameIndex");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity
                        .HasNoKey()
                        .ToTable("ChatLiveInfo");
    
                    entity.HasIndex(e => new { e.RoomName, e.LiveId }, "IX_ChatLiveInfo_RoomName_LiveId").IsUnique();
    
                    entity.HasIndex(e => e.LiveId, "ChatLiveInfoLiveIdIdx");
    
                    entity.HasIndex(e => e.RoomName, "ChatLiveInfoRoomNamex");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.ChatRoomName);
    
                    entity.ToTable("ChatRoom");
    
                    entity.Property(e => e.ChatRoomFlag)
                        .HasDefaultValueSql("0")
                        .HasColumnType("INT");
                    entity.Property(e => e.IsShowName).HasDefaultValueSql("0");
                    entity.Property(e => e.Owner).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved7).HasDefaultValueSql("0");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.ChatRoomName);
    
                    entity.ToTable("ChatRoomInfo");
    
                    entity.Property(e => e.AnnouncementPublishTime).HasDefaultValueSql("0");
                    entity.Property(e => e.ChatRoomStatus).HasDefaultValueSql("0");
                    entity.Property(e => e.InfoVersion).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved7).HasDefaultValueSql("0");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity
                        .HasNoKey()
                        .ToTable("ChatroomTool");
    
                    entity.HasIndex(e => e.ChatroomUsername, "IX_ChatroomTool_ChatroomUsername").IsUnique();
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.UserName);
    
                    entity.ToTable("Contact");
    
                    entity.HasIndex(e => e.Reserved2, "Contact_Idx0");
    
                    entity.Property(e => e.ChatRoomNotify).HasDefaultValueSql("0");
                    entity.Property(e => e.ChatRoomType).HasColumnType("INT");
                    entity.Property(e => e.DelFlag).HasDefaultValueSql("0");
                    entity.Property(e => e.LabelIdlist).HasColumnName("LabelIDList");
                    entity.Property(e => e.Pyinitial).HasColumnName("PYInitial");
                    entity.Property(e => e.RemarkPyinitial).HasColumnName("RemarkPYInitial");
                    entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved2).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved5).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved8).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved9).HasDefaultValueSql("0");
                    entity.Property(e => e.Type).HasDefaultValueSql("0");
                    entity.Property(e => e.VerifyFlag).HasDefaultValueSql("0");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.UsrName);
    
                    entity.ToTable("ContactHeadImgUrl");
    
                    entity.HasIndex(e => e.Reverse0, "reverse0Index");
    
                    entity.Property(e => e.UsrName).HasColumnName("usrName");
                    entity.Property(e => e.BigHeadImgUrl).HasColumnName("bigHeadImgUrl");
                    entity.Property(e => e.HeadImgMd5).HasColumnName("headImgMd5");
                    entity.Property(e => e.Reverse0)
                        .HasColumnType("INT")
                        .HasColumnName("reverse0");
                    entity.Property(e => e.Reverse1).HasColumnName("reverse1");
                    entity.Property(e => e.SmallHeadImgUrl).HasColumnName("smallHeadImgUrl");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.LabelId);
    
                    entity.ToTable("ContactLabel");
    
                    entity.Property(e => e.LabelId).ValueGeneratedNever();
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity
                        .HasNoKey()
                        .ToTable("DelayDownLoad");
    
                    entity.HasIndex(e => e.MessageServId, "IX_DelayDownLoad_MessageServId").IsUnique();
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity
                        .HasNoKey()
                        .ToTable("FTSChatroomTrans");
    
                    entity.Property(e => e.DisplayName).HasColumnName("displayName");
                    entity.Property(e => e.GroupUsername).HasColumnName("groupUsername");
                    entity.Property(e => e.Nickname).HasColumnName("nickname");
                    entity.Property(e => e.Operation).HasColumnName("operation");
                    entity.Property(e => e.Reserve1).HasColumnName("reserve1");
                    entity.Property(e => e.Reserve2).HasColumnName("reserve2");
                    entity.Property(e => e.Username).HasColumnName("username");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity
                        .HasNoKey()
                        .ToTable("FTSContactTrans");
    
                    entity.Property(e => e.Reserve1).HasColumnName("reserve1");
                    entity.Property(e => e.Reserve2).HasColumnName("reserve2");
                    entity.Property(e => e.Username).HasColumnName("username");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.Key);
    
                    entity.ToTable("MainConfig");
    
                    entity.HasIndex(e => e.Reserved0, "MainConfigReserved0Idx");
    
                    entity.HasIndex(e => e.Reserved1, "MainConfigReserved1Idx");
    
                    entity.Property(e => e.Reserved0).HasColumnType("INT");
                    entity.Property(e => e.Reserved1).HasColumnType("INT");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.ToTable("OpLog");
    
                    entity.Property(e => e.Id)
                        .ValueGeneratedNever()
                        .HasColumnName("ID");
                    entity.Property(e => e.CmditemBuffer).HasColumnName("CMDItemBuffer");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.Username);
    
                    entity.ToTable("PatInfo");
    
                    entity.Property(e => e.Username).HasColumnName("username");
                    entity.Property(e => e.Reserved1)
                        .HasDefaultValueSql("0")
                        .HasColumnName("reserved1");
                    entity.Property(e => e.Reserved2)
                        .HasDefaultValueSql("0")
                        .HasColumnName("reserved2");
                    entity.Property(e => e.Reserved3)
                        .HasDefaultValueSql("0")
                        .HasColumnName("reserved3");
                    entity.Property(e => e.Reserved4)
                        .HasDefaultValueSql("0")
                        .HasColumnName("reserved4");
                    entity.Property(e => e.Reserved5).HasColumnName("reserved5");
                    entity.Property(e => e.Reserved6).HasColumnName("reserved6");
                    entity.Property(e => e.Reserved7).HasColumnName("reserved7");
                    entity.Property(e => e.Reserved8).HasColumnName("reserved8");
                    entity.Property(e => e.Reserved9).HasColumnName("reserved9");
                    entity.Property(e => e.Suffix).HasColumnName("suffix");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.CreateTime);
    
                    entity.ToTable("RevokeMsgStorage");
    
                    entity.HasIndex(e => e.MsgSvrId, "MsgSvrId_Idx");
    
                    entity.HasIndex(e => e.RevokeSvrId, "RevokeSvrID_Idx");
    
                    entity.Property(e => e.CreateTime).ValueGeneratedNever();
                    entity.Property(e => e.MsgSvrId)
                        .HasColumnType("INTERGER")
                        .HasColumnName("MsgSvrID");
                    entity.Property(e => e.RevokeSvrId)
                        .HasColumnType("INTERGER")
                        .HasColumnName("RevokeSvrID");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.StrUsrName);
    
                    entity.ToTable("Session");
    
                    entity.HasIndex(e => e.NOrder, "nOrderIndex");
    
                    entity.Property(e => e.StrUsrName).HasColumnName("strUsrName");
                    entity.Property(e => e.BytesXml).HasColumnName("bytesXml");
                    entity.Property(e => e.EditContent).HasColumnName("editContent");
                    entity.Property(e => e.NIsSend).HasColumnName("nIsSend");
                    entity.Property(e => e.NMsgLocalId).HasColumnName("nMsgLocalID");
                    entity.Property(e => e.NMsgStatus).HasColumnName("nMsgStatus");
                    entity.Property(e => e.NMsgType).HasColumnName("nMsgType");
                    entity.Property(e => e.NOrder)
                        .HasDefaultValueSql("0")
                        .HasColumnType("INT")
                        .HasColumnName("nOrder");
                    entity.Property(e => e.NStatus).HasColumnName("nStatus");
                    entity.Property(e => e.NTime).HasColumnName("nTime");
                    entity.Property(e => e.NUnReadCount)
                        .HasDefaultValueSql("0")
                        .HasColumnName("nUnReadCount");
                    entity.Property(e => e.OthersAtMe)
                        .HasColumnType("INT")
                        .HasColumnName("othersAtMe");
                    entity.Property(e => e.ParentRef).HasColumnName("parentRef");
                    entity.Property(e => e.Reserved0).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved2).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved4).HasDefaultValueSql("0");
                    entity.Property(e => e.StrContent).HasColumnName("strContent");
                    entity.Property(e => e.StrNickName).HasColumnName("strNickName");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.UserName);
    
                    entity.ToTable("TicketInfo");
    
                    entity.Property(e => e.Reserved1).HasDefaultValueSql("0");
                    entity.Property(e => e.Reserved3).HasDefaultValueSql("0");
                });
    
                modelBuilder.Entity(entity =>
                {
                    entity.HasKey(e => e.MsgId);
    
                    entity.ToTable("TopStoryReddotInfo");
    
                    entity.Property(e => e.H5version).HasColumnName("H5Version");
                });
    
                OnModelCreatingPartial(modelBuilder);
            }
    
            partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
        }
    }
    复制代码

    结尾

        在本文中,我们一共讲了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

  • 相关阅读:
    项目上线后我是如何通过慢查询和索引让系统快起来的
    CMOS图像传感器——Remosaic技术
    用Python绘制分子结构
    【纯css】实现无限滚动循环组件,无js
    【SpringMVC】拦截器&JSR303的使用
    NAACL22 | 引入多模态对比学习来增强句子特征学习
    [R] How to communicate with your data? - ggplot2
    redis大key优化
    Vue实现Antv/X6中的示例,以及一些er图开发场景
    NTU20220813-数据结构化和深度学习-WSL音频转发
  • 原文地址:https://www.cnblogs.com/1996-Chinese-Chen/p/17761733.html