EFCore查缺补漏(一):依赖注入

前段时间,在群里潜水的时候,看见有个群友的报错日志是这样的:

An unhandled exception was thrown by the application. System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.at System.Threading.Thread.StartInternal()at Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider..ctor(IOptionsMonitor`1 options)at …at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)at …at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)at Microsoft.Extensions.Logging.LoggerFactory.Create(Action`1 configure)at xxxxxxx. <>c__DisplayClass2_0.<AddXxxDbContext>b__0(DbContextOptionsBuilder builder)at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.CreateDbContextOptions[TContext](IServiceProvider applicationServiceProvider, Action`2 optionsAction)at …

嗯……内存满了?是在构建 ConsoleLoggerProvider 的时候报的异常?是由依赖注入容器产生的?再上层是 AddXxxDbContext?

好吧,看来一定是位没研究过 EFCore 源码也没看过与本文类似内容的仁兄……我甚至能反推出他写的代码:

public class Startup
{public void ConfigureServices(IServiceCollection services){services.AddDbContext<MyDbContext>(options =>{// ...options.UseLoggerFactory(LoggerFactory.Create(b => b.AddConsole().AddDebug()));});// ...}// ...
}

C#

看,这个调用堆栈是不是对上味儿了。

接下来我将介绍这个bug产生的原因,并带各位看官一窥 DbContext、DbContextOptions、EFCore内部类的大致生命周期。

本文所有知识均基于 EFCore 3.1 版本,EFCore 5.0 对这部分几乎没有改动。

另外,如果有兴趣调试 EFCore 的源码,可以 clone 下来某个 release 版本,然后保留 EFCore/Abstractions/Analyzers/Relational/SqlServer 这几个项目,然后开一个自己的命令行或者单元测试项目,就可以尽情遨游 EFCore 的源码了。

读代码前,请储备一定量的英文知识和自信。很多代码的意思都写在变量名和函数名上了,大部分源代码读起来并不是什么很难的事情:)

谁实例化了 DbContext?

常见有两种方式来构建 DbContext,一种是直接拿来 new 一个,构造函数传入 DbContextOptions 或者什么都不传入;一种是在 ASP.NET Core 中常用的 services.AddDbContext<...>(...),然后通过某个服务的构造函数或者 IServiceProvider 取得该 DbContext 实例。后者要求该 DbContext 只实现一个构造函数,该构造函数只接受一个参数 DbContextOptions<MyDbContext>

关于后一种构造方式,我们将父依赖注入容器称为 Application ServiceProvider。

首先需要明确的一点是,DbContext 的构造是由父依赖注入容器实现的。而构造函数要求检测仅仅是 EFCore 那个拓展函数进行的检查。

我们先来看各个 AddDbContext 的核心操作函数吧。

public static IServiceCollection AddDbContext<TContextService, TContextImplementation>([NotNull] this IServiceCollection serviceCollection,[CanBeNull] Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,ServiceLifetime contextLifetime = ServiceLifetime.Scoped,ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)where TContextImplementation : DbContext, TContextService
{Check.NotNull(serviceCollection, nameof(serviceCollection));if (contextLifetime == ServiceLifetime.Singleton){optionsLifetime = ServiceLifetime.Singleton;}if (optionsAction != null){CheckContextConstructors<TContextImplementation>();}AddCoreServices<TContextImplementation>(serviceCollection, optionsAction, optionsLifetime);serviceCollection.TryAdd(new ServiceDescriptor(typeof(TContextService), typeof(TContextImplementation), contextLifetime));return serviceCollection;
}

C#

Copy

在这里可以看到:

  • 我们可以修改 DbContextOptions 和 DbContext 的生命周期为 Singleton 或者 Transient,而不是默认的 Scoped

  • 当检测到对 DbContextOptionsBuilder 的调用时,会检查构造函数是否符合要求

  • TContextImplementation 是被构造的 DbContext 实例类型,直接由该依赖注入容器构造

而 AddCoreServices 函数则是将 DbContextOptions 实例注入容器。

private static void AddCoreServices<TContextImplementation>(IServiceCollection serviceCollection,Action<IServiceProvider, DbContextOptionsBuilder> optionsAction,ServiceLifetime optionsLifetime)where TContextImplementation : DbContext
{serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbContextOptions<TContextImplementation>),p => CreateDbContextOptions<TContextImplementation>(p, optionsAction),optionsLifetime));serviceCollection.Add(new ServiceDescriptor(typeof(DbContextOptions),p => p.GetRequiredService<DbContextOptions<TContextImplementation>>(),optionsLifetime));
}

C#

Copy

在这里可以看到:

  • 容器中可能具有很多个 DbContextOptions 实例,可以通过 IEnumerable<DbContextOptions> 拿到全部;这一设计是由于一个依赖注入容器中可以加入多个 DbContext 类型

  • 对于每一个特性类型的 DbContext (以下写为 MyDbContext),都会有一个 DbContextOptions<MyDbContext> 与之对应

  • 我们在构造函数处用到的 DbContextOptionsBuilder 和 Microsoft.Extensions.Options 其实没什么关系,不能用 IOptions<TOptions> 拿到,只是恰巧都叫 XxxxxxOptions 而已

  • 每次新构造 DbContextOptions 实例时,都会使用传入的 Action<IServiceProvider, DbContextOptionsBuilder> 函数;此时第一个参数显然是当前的依赖注入容器,例如发生 HTTP 请求时 HttpContext.RequestService 的容器 Scope;或者 DbContextOptions 单例注入时, IHost.Services 这种容器根

  • 实际构建结果是由 CreateDbContextOptions 函数创造的

那么再来看看 CreateDbContextOptions 的实现。

private static DbContextOptions<TContext> CreateDbContextOptions<TContext>([NotNull] IServiceProvider applicationServiceProvider,[CanBeNull] Action<IServiceProvider, DbContextOptionsBuilder> optionsAction)where TContext : DbContext
{var builder = new DbContextOptionsBuilder<TContext>(new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>()));builder.UseApplicationServiceProvider(applicationServiceProvider);optionsAction?.Invoke(applicationServiceProvider, builder);return builder.Options;
}

C#

Copy

可以看到,DbContextOptionsBuilder.UseApplicationServiceProvider 实际上是被执行过的,并且恰好指向父依赖注入容器。

此时会发现,我们在单元测试时,不创建依赖注入容器而直接实例化 DbContext 的时候,是没有这一步的。这就是为什么两者有时表现不同,例如直接实例化 Builder 拿到 Options,并且没有 UseLoggerFactory 和 UseApplicationServiceProvider 时,它不会有日志输出。至于日志那部分是怎么构建的呢,暂且按下不表。

而我们会看到网上有些文章说,因为某某原因,选择 services.AddEntityFrameworkSqlServer() 然后 options.UseInternalServiceProvider(..) 的,其实是将两个依赖注入容器合二为一了。具体好坏,还是使用者自行定夺。

DbContext 实例化时做了些什么?

看到上面那个图了吗。我们会发现,原来 EFCore 的内部容器也是分 Singleton 和 Scoped 的。

先来看看 DbContext 的这样一个 private 成员属性 InternalServiceProvider。

private IServiceProvider InternalServiceProvider
{get{CheckDisposed();if (_contextServices != null){return _contextServices.InternalServiceProvider;}if (_initializing){throw new InvalidOperationException(CoreStrings.RecursiveOnConfiguring);}try{_initializing = true;var optionsBuilder = new DbContextOptionsBuilder(_options);OnConfiguring(optionsBuilder);if (_options.IsFrozen&& !ReferenceEquals(_options, optionsBuilder.Options)){throw new InvalidOperationException(CoreStrings.PoolingOptionsModified);}var options = optionsBuilder.Options;_serviceScope = ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: true).GetRequiredService<IServiceScopeFactory>().CreateScope();var scopedServiceProvider = _serviceScope.ServiceProvider;var contextServices = scopedServiceProvider.GetService<IDbContextServices>();contextServices.Initialize(scopedServiceProvider, options, this);_contextServices = contextServices;DbContextDependencies.InfrastructureLogger.ContextInitialized(this, options);}finally{_initializing = false;}return _contextServices.InternalServiceProvider;}
}

C#

Copy

可以观察到如下事实:

  • 除了外部的 DbContextOptions 实例,内部可能也会用 OnConfiguring 函数修改这个 Options,这样保证了两者的配置都会被应用;当使用 DbContextPool 时,内部函数是不能修改配置的

  • DbContext 的每个执行指令都是在内部容器的一个 Service Scope 中执行

  • 每次创建 Service Scope 之后,会取出其中 Scoped 服务 IDbContextServices,并将这个 DbContext 实例和 DbContextOptions 保存进这个 Service Scope

  • 内部容器的获取是由 ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: true) 操作的;此时拿到的一般都是内部容器的根容器

这个 ServiceProviderCache 的源码处于 src\EFCore\Internal\ServiceProviderCache.cs

在解析 GetOrAdd 函数之前,我们需要了解这样一个结构:IDbContextOptionsExtension。这个结构具有几个基本功能:

  • 向依赖注入容器注册依赖服务

  • 验证当前 IDbContextOptions 是否正确配置,是否具有冲突

  • 告诉 EFCore 该拓展是否提供数据库底层功能(即 Database Provider,例如提供 SQL Server 相关依赖、数据库连接信息等)

  • 提供调试信息、日志片段(就是初始化 DbContext 时出现的类似 initialized 'MyDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options:... 的地方添加的)

  • 实现函数 long GetServiceProviderHashCode(),当这个 EFCore 插件包括某些不太方便通过 Scoped 服务修改的 Singleton 信息时(例如 SensitiveDataLoggingEnabled),这里应该返回一个与这些配置有关的值,同时保证:对于相同的配置,返回相同的值;对于不同的配置,返回不同的值。

例如 DbContextOptionsBuilder 中很多函数都是修改 CoreOptionsExtension 完成的。

再看看 EFCore 的内部容器中有哪些类,其对应生命周期是什么样的。此处建议参考 src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs。这个代码文件中规定了每个类的生命周期,以及是否可以注册多个。

可以注意到,有这样一些类有着对应的生命周期:

Singleton:
- IDatabaseProvider
- IDbSetFinder
- IModelCustomizer
- ILoggingOptions
- IMemoryCacheScoped:
- IInterceptors
- ILoggerFactory
- IModel
- IDbContextServices
- IChangeTrackerFactory
- IDiagnosticsLogger<>
- IQueryCompiler
- IQueryContextFactory
- IAsyncQueryProvider
- ICurrentDbContext
- IDbContextOptions

接下来看拿到内部容器的逻辑。

public virtual IServiceProvider GetOrAdd([NotNull] IDbContextOptions options, bool providerRequired)
{var coreOptionsExtension = options.FindExtension<CoreOptionsExtension>();var internalServiceProvider = coreOptionsExtension?.InternalServiceProvider;if (internalServiceProvider != null){ValidateOptions(options);var optionsInitializer = internalServiceProvider.GetService<ISingletonOptionsInitializer>();if (optionsInitializer == null){throw new InvalidOperationException(CoreStrings.NoEfServices);}if (providerRequired){optionsInitializer.EnsureInitialized(internalServiceProvider, options);}return internalServiceProvider;}if (coreOptionsExtension?.ServiceProviderCachingEnabled == false){return BuildServiceProvider().ServiceProvider;}var key = options.Extensions.OrderBy(e => e.GetType().Name).Aggregate(0L, (t, e) => (t * 397) ^ ((long)e.GetType().GetHashCode() * 397) ^ e.Info.GetServiceProviderHashCode());return _configurations.GetOrAdd(key, k => BuildServiceProvider()).ServiceProvider;(IServiceProvider ServiceProvider, IDictionary<string, string> DebugInfo) BuildServiceProvider(){... 此处省略}
}

C#

Copy

嗯,这个逻辑很好盘,而且 99.99% 的情况下大家都只使用了默认配置,即:通过 GetServiceProviderHashCode 函数来计算哈希值,然后从 ServiceProviderCache 内部的一个缓存表中取得之前创建的容器,或者构建一个新的容器。

我们可能会发现,第一次使用 DbContext 的时候,加载时间很长;经过两三秒才能实例化完成;第二次使用的时候,基本上就是瞬间实例化成功了。但我们通过在上层依赖注入容器的 AddDbContext 处做手脚,或者通过重写 OnConfiguring 函数,更改了 DbContextOptions 之后,或者实例化另一个不同类型的 DbContext,又会花很久时间才能实例化成功。应证了上面的说法。

如果每次构建 DbContext 实例时都创建一个全新的内部容器,这样会有大量的性能浪费。

那么我们再来观察一下 DbContextOptionsBuilder 有哪些方法。

- UseSqlServer / UseNpgSql / UseInMemoryDatabase
- Use第三方插件1/2/3
- EnableDetailedErrors
- UseInternalServiceProvider
- EnableSensitiveDataLogging
- EnableServiceProviderCaching
- ConfigureWarnings
- UseMemoryCache
- ReplaceService
--- 一条朴实无华的分割线 ---
- UseModel
- UseLoggerFactory
- UseApplicationServiceProvider
- UseQueryTrackingBehavior
- AddInterceptors

CoreOptionsExtension 的 long GetServiceProviderHashCode() 会包括 IMemoryCacheSensitiveDataLoggingEnabledDetailedErrorsEnabledWarningsConfiguration、通过 ReplaceService 修改的那些服务。

可以注意到,其中有些控制的是 Singleton 服务或者决定了实例化的结果,例如 UseMemoryCacheUseSqlServerReplaceService,如果每次拿到的 DbContextOptions 实例中的 IMemoryCache 或者数据库类型不一样,那么此时肯定需要构建一个新的依赖注入容器。而有些东西控制的是 Scoped 服务,例如 UseLoggerFactoryUseModel、数据库连接字符串,在一般场景下是不需要重新构建容器的。

也就是说,如果不动态改变分割线上方的那些状态,并且你使用的第三方插件编写很科学,是不会每次都构建新的内部容器的。

内部容器如何取得 ILoggerFactory?

内部的服务当然是从内部容器构建的了。

先以 ILoggerFactory 为例,看看为什么 EFCore 能拿到父容器的 ILoggerFactory

回到上面 EntityFrameworkServicesBuilder,我们可以看到一行

TryAdd<ILoggerFactory>(p => ScopedLoggerFactory.Create(p, null));

C#

Copy

转到这个函数,我们可以看到

public static ScopedLoggerFactory Create([NotNull] IServiceProvider internalServiceProvider,[CanBeNull] IDbContextOptions contextOptions)
{var coreOptions= (contextOptions ?? internalServiceProvider.GetService<IDbContextOptions>())?.FindExtension<CoreOptionsExtension>();if (coreOptions != null){if (coreOptions.LoggerFactory != null){return new ScopedLoggerFactory(coreOptions.LoggerFactory, dispose: false);}var applicationServiceProvider = coreOptions.ApplicationServiceProvider;if (applicationServiceProvider != null&& applicationServiceProvider != internalServiceProvider){var loggerFactory = applicationServiceProvider.GetService<ILoggerFactory>();if (loggerFactory != null){return new ScopedLoggerFactory(loggerFactory, dispose: false);}}}return new ScopedLoggerFactory(new LoggerFactory(), dispose: true);
}

C#

Copy

即:先看 CoreOptionsExtension 中是否有之前 optionsBuilder.UseLoggerFactory 指定的;如果没有,再到 ApplicationServiceProvider 中找一个 ILoggerFactory;再如果真的没有,就不用了。

回顾开头的内存溢出问题:为什么呢?

DbContextOptions 未经修改的默认生命周期是 Scoped,也就是在父容器中每次实例化一个 DbContextOptions,就会调用一次 LoggerFactory.Create(b => b.AddConsole()),并且并没有照顾到它的 Dispose。而 ConsoleLoggerProvider 每次会建立一个新的线程去输出日志,没有被回收,于是……内存就在一次又一次请求中消耗殆尽了。

再回过来想想,既然能调用到父容器的 ILoggerFactory,他又为什么会用 LoggerFactory.Create 呢?……一定是 Microsoft.EntityFrameworkCore 开头的日志被父容器的设置禁用了,所以没有输出。

如何把玩其他内部服务?

观察到 DbContext 实现了 IInfrastructure<IServiceProvider> 这一接口,这个接口要求保存一个 IServiceProvider 的实例,而其实现直接指向了 InternalServiceProvider 这一私有属性。

那先谈谈这个 IInfrastructure<IServiceProvider> 接口的作用吧。这个接口同时在 DbSet<T> 和 DatabaseFacade 中也有实现。在 Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions 中,我们有一个针对这个接口的拓展函数 TService GetService<TService>([NotNull] this IInfrastructure<IServiceProvider> accessor)

也就是说,我们在引入 Microsoft.EntityFrameworkCore.Infrastructure 命名空间之后,可以通过 DbContext.GetService<T>() 来拿到一部分服务。

其进一步的查找逻辑为:先在 EFCore 内部直接使用的依赖注入容器(即 InternalServiceProvider)中查找,再去上一层依赖注入容器中查找。

这个函数在 EFCore 中用的很少,基本上只用于静态函数,或者非静态函数中传入 DbContext 实例时需要拿到某个服务时才会用到。

例如,如果是在写某个 EFCore 的拓展函数,传入只有 DbSet<T> 的实例,但我们想拿到这个 DbContext,不用反射之类的奇怪功能,要如何拿到呢?通常可以用 dbSetInstance.GetService<ICurrentDbContext>().Context 拿到实例。

好了,容器都拿到了,该咋玩咋玩吧……

课后习题

已知数据库模型是通过 IModelCustomizer 进行构建的,需要达到这样的效果:

  • 一个模块化的应用

  • 每个模块可以向父容器注册很多个功能类似于 Action<ModelBuilder> 的东西

  • 希望在构建数据库的 IModel 时,对着 ModelBuilder 执行这些操作

这样可以不修改 DbContext 本身的代码,而将所需的实体信息加载到 DbContext 的 Model 里。

参考答案:IDbModelSupplier设计 + AddDbContext部分

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305149.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql倒序排列取第一条_从零学会SQL·三——汇总分析

一、常用汇总函数的练习score表问题&#xff1a;查询课程编号为“0002”的总成绩查询选了课程的学生人数查询学号为“0001”的学生的最高分、最低分和平均分解答SQL语句如下&#xff1a;-- 查询课程编号为“0002”的总成绩 SELECT 学号,SUM(成绩)FROM score WHERE 课程号0002;-…

2017 软件开发薪酬调查:Go 和 Scala 是最赚钱的语言

英文&#xff1a;codeburst&#xff0c;编译&#xff1a;oschina www.oschina.net/news/87499/go-scala-are-the-most-lucrative-languages 每年 O’Reilly 都会发布其年度开发者的调查结果。这项调查涵盖了来自世界 110 个国家的近 7000 名程序员。这里摘几个亮点&#xff1a;…

bootstrap grid php,bootstrap grid用法

bootstrap grid的用法&#xff1a;首先使用container来包裹div&#xff1b;然后在div里面设置行&#xff1b;接着设置列能够快速对这个框架进行搭建&#xff1b;最后通过拖拽浏览器来改变宽窄即可。本文操作环境&#xff1a;Windows7系统、bootstrap3、Dell G3电脑。bootstrap的…

js map对象遍历_前端测试题:有关于js中跨域请求的说法,错误的是?

考核内容:javascript 跨域的使用题发散度: ★试题难度: ★解题思路:什么是跨域&#xff1f;跨域是指一个域下的文档或脚本试图去请求另一个域下的资源&#xff0c;这里跨域是广义的。什么是同源策略&#xff1f;同源策略/SOP(Same origin policy)是一种约定&#xff0c;由Netsc…

管理信息系统案例分析_「案例」MES系统助力华联电子仓库工作人员效率提高30%...

一、企业简介厦门华联电子股份有限公司成立于1984年&#xff0c;注册资金12929万元&#xff0c;现有自主厂房8万平方米&#xff0c;全自动化生产线40多条&#xff0c;员工2400余人&#xff0c;是国内最具规模的智能控制器、光电子器件的生产厂家之一。企业集研发、生产、销售和…

数学系学生的漫画,治愈了整个朋友圈

如何在无趣的世界里&#xff0c;做一个有趣的人&#xff1f; 有时候&#xff0c;无厘头、无意义、有趣性&#xff0c;胜过一切宏大叙事。 “此人脑洞开得很大&#xff0c;且深不见底。”这是网友对tango的描述。 对于画家tango的作品&#xff0c;大家并不陌生&#xff0c;最近他…

孟岩谈Erlang:并行计算和云计算

孟岩谈Erlang&#xff1a;并行计算和云计算 ——写在《Erlang程序设计》出版之际 Erlang算不上是一种“大众流行”的程序设计语言&#xff0c;而且即使是Erlang的支持者&#xff0c;大多数也对于Erlang成为“主流语言”并不持乐观态度。然而&#xff0c;自从2006年以来&#xf…

php获取域名方法,PHP实现获取域名的方法小结

文章主要介绍了PHP实现获取域名的方法,实例总结了常见的获取域名的方法,非常具有实用价值,需要的朋友可以参考下。方法一(用 系统变量)复制代码 代码如下://缺点不使用传递过来的地址和不支持系统变量的主机echo $_SERVER[HTTP_HOST];方法二(用自带函数)复制代码 代码如下:$url…

逻辑回归算法背后的数学

看完Andrew Ng老师的机器学习公开课后&#xff0c;对于逻辑回归部分&#xff0c;打算写篇学习笔记记录总结一下&#xff0c;也和大家共同分享。 1 基本思能 逻辑回归&#xff08;Logistic Regression&#xff09;和线性回归&#xff08;Linear Regression&#xff09;的模型和原…

配置DNS辅助服务器:DNS系列之四

配置DNS辅助服务器在前面的博文中&#xff0c;我们介绍了如何在DNS服务器中创建常用的DNS记录&#xff0c;本文中我们要为大家介绍如何配置DNS的辅助服务器&#xff0c;同时也要介绍一下和辅助区域类似的存根区域。DNS辅助服务器是一种容错设计&#xff0c;考虑的是一旦DNS主服…

笑出腹肌的程序猿搞笑趣图

客户需求 vs 最终产品 requirements vs. implementation 程序员的一天 The Programmers life 寂寞的时候干什么&#xff1f; 写程序写程序写程序 失恋的时候干什么&#xff1f; 写程序写程序写程序 发骚的时候干什么&#xff1f; 写程序写程序写程序 剩下的时候干什么&#xff…

Java图形 图像与多媒体基础,十一. 图形、图像与多媒体1.绘图基础

要在平面上显示文字和绘图&#xff0c;首先要确定一个平面坐标系。Java语言约定&#xff0c;显示屏上一个长方形区域为程序绘图区域&#xff0c;坐标原点(0,0)位于整个区域的左上角。一个坐标点(x,y)对应屏幕窗口中的一个像素&#xff0c;是整数。如图12.1所示。窗口大小由超文…

程序员快来看!经典代码替你省去多少时间?

作为一名程序员&#xff0c;每天坐在电脑前敲敲打打那些重复重复再重复的语句&#xff0c;习惯性的思维总是被套进去&#xff0c;有些时候很容易把简单的事情想复杂了&#xff01;不过&#xff0c;我们反其道而行之&#xff0c;新手们只要是把下面的语句牢记&#xff0c;很多情…

php中sql删除,学习猿地-php sql删除语句是什么

php sql删除语句是“DELETE FROM”&#xff0c;该语句用于从数据库表中删除行&#xff0c;其语法是“DELETE FROM table_name WHERE column_name some_value”。PHP MySQL Delete FromDELETE FROM 语句用于从数据库表中删除行。删除数据库中的数据DELETE FROM 语句用于从数据库…

一张图看懂新一代人工智能知识体系

关于人工智能的前世今生、内涵意义&#xff0c;下图可以说是相当清楚全面了。人工智能是未来一大热点&#xff0c;连腾讯、阿里、百度这些科技公司都各自成立了人工智能实验室&#xff0c;如果你也看好这一趋势&#xff0c;不妨把这张图收藏起来慢慢看。 来源&#xff1a;智能…

android java 面试题,Android java 高级面试题库

(一) java基础面试知识点1&#xff0c;java中和equals和hashCode的区别&#xff1f;1、“”是运算符&#xff0c;用来比较两个值、两个对象的内存地址是否相等。2、“equals()”&#xff1a;equals是Object类的方法&#xff0c;默认情况下比较两个对象是否是同一个对象&#xf…

促进新一代人工智能产业发展三年行动计划_工信部新一代人工智能产业创新重点揭榜任务——中国联通智能化网络基础设施及开放平台启动会成功召开...

4月2日&#xff0c;工信部新一代人工智能产业创新重点揭榜任务(下称“人工智能重点揭榜任务”)——中国联通智能化网络基础设施及开放平台在线启动会成功召开。来自中国联通网络技术研究院、联通集团智能网络中心网络AI中心、中国联通智能城市研究院、广东联通、中讯邮电咨询设…

数学家破解婚恋网站配对程序,90天找到灵魂伴侣

找个合适或者善良的另一半比什么都强&#xff0c;谁说搞技术的情商低&#xff1f;他们可以靠高超的技能来弥补&#xff0c;照样能找到优秀的灵魂伴侣&#xff01; 凌晨三点&#xff0c;在加州大学洛杉矶分校&#xff08;UCLA&#xff09;数学系大楼五楼的一个拥挤小隔间里&…

Appointment over SMS on Windows Mobile

设想这样一个场景&#xff0c;早上出门的时候&#xff0c;手机里一个Appointment提醒弹了出来&#xff0c;恰好昨天还没有来得及通知参与该会议的几个重要成员&#xff0c;而这时候身边又没有可以接入的Wi-Fi。这时&#xff0c;给成员们发送一条短信提醒一下&#xff0c;也算是…

oracle grid需要安装,Oracle 11g Grid for Linux安装指南

Oracle 11g Grid for Linux安装指南1、创建oracle用户及用户组groupadd oinstallgroupadd dbauseradd -g oinstall -G dba -m oracle设置oracle用户的口令:passwd oracle我这里设置为oracle2、创建相关目录mkdir -p /u01chown -R oracle.oinstall /u013、修改oracle用户环境变量…