- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章ASP.NET Core Controller与IOC结合问题整理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
看到标题可能大家会有所疑问Controller和IOC能有啥羁绊,但是我还是拒绝当一个标题党的。相信有很大一部分人已经知道了这么一个结论,默认情况下ASP.NET Core的Controller并不会托管到IOC容器中,注意关键字我说的是"默认",首先咱们不先说为什么,如果还有不知道这个结论的同学们可以自己验证一下,验证方式也很简单,大概可以通过以下几种方式.
首先,我们可以尝试在ServiceProvider中获取某个Controller实例,比如 。
1
2
3
4
|
public
void
Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var productController = app.ApplicationServices.GetService<ProductController>();
}
|
这是最直接的方式,可以在IOC容器中获取注册过的类型实例,很显然结果会为null。另一种方式,也是利用它的另一个特征,那就是通过构造注入的方式,如下所示我们在OrderController中注入ProductController,显然这种方式是不合理的,但是为了求证一个结果,我们这里仅做演示,强烈不建议实际开发中这么写,这是不规范也是不合理的写法 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
OrderController : Controller
{
private
readonly
ProductController _productController;
public
OrderController(ProductController productController)
{
_productController = productController;
}
public
IActionResult Index()
{
return
View();
}
}
|
结果显然是会报一个错InvalidOperationException: Unable to resolve service for type 'ProductController' while attempting to activate 'OrderController'。原因就是因为ProductController并不在IOC容器中,所以通过注入的方式会报错。还有一种方式,可能不太常用,这个是利用注入的一个特征,可能有些同学已经了解过了,那就是通过自带的DI,即使一个类中包含多个构造函数,它也会选择最优的一个,也就是说自带的DI允许类包含多个构造函数。利用这个特征,我们可以在Controller中验证一下 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
OrderController : Controller
{
private
readonly
IOrderService _orderService;
private
readonly
IPersonService _personService;
public
OrderController(IOrderService orderService)
{
_orderService = orderService;
}
public
OrderController(IOrderService orderService, IPersonService personService)
{
_orderService = orderService;
_personService = personService;
}
public
IActionResult Index()
{
return
View();
}
}
|
我们在Controller中编写了两个构造函数,理论上来说这是符合DI特征的,运行起来测试一下,依然会报错InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'OrderController'. There should only be one applicable constructor。以上种种都是为了证实一个结论,默认情况下Controller并不会托管到IOC当中.
上面虽然我们看到了一些现象,能说明Controller默认情况下并不在IOC中托管,但是还没有足够的说服力,接下来我们就来查看源码,这是最有说服力的。我们找到Controller工厂注册的地方,在MvcCoreServiceCollectionExtensions扩展类中[点击查看源码]的AddMvcCoreServices方法里 。
1
2
3
4
5
|
//给IControllerFactory注册默认的Controller工厂类DefaultControllerFactory
//也是Controller创建的入口
services.TryAddSingleton<IControllerFactory, DefaultControllerFactory>();
//真正创建Controller的工作类DefaultControllerActivator
services.TryAddTransient<IControllerActivator, DefaultControllerActivator>();
|
由此我们可以得出,默认的Controller创建工厂类为DefaultControllerFactory,那么我们直接找到源码位置[点击查看源码], 为了方便阅读,精简一下源码如下所示 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
internal
class
DefaultControllerFactory : IControllerFactory
{
//真正创建Controller的工作者
private
readonly
IControllerActivator _controllerActivator;
private
readonly
IControllerPropertyActivator[] _propertyActivators;
public
DefaultControllerFactory(
IControllerActivator controllerActivator,
IEnumerable<IControllerPropertyActivator> propertyActivators)
{
_controllerActivator = controllerActivator;
_propertyActivators = propertyActivators.ToArray();
}
/// <summary>
/// 创建Controller实例的方法
/// </summary>
public
object
CreateController(ControllerContext context)
{
//创建Controller实例的具体方法(这是关键方法)
var controller = _controllerActivator.Create(context);
foreach
(var propertyActivator
in
_propertyActivators)
{
propertyActivator.Activate(context, controller);
}
return
controller;
}
/// <summary>
/// 释放Controller实例的方法
/// </summary>
public
void
ReleaseController(ControllerContext context,
object
controller)
{
_controllerActivator.Release(context, controller);
}
}
|
用过上面的源码可知,真正创建Controller的地方在_controllerActivator.Create方法中,通过上面的源码可知为IControllerActivator默认注册的是DefaultControllerActivator类,直接找到源码位置[点击查看源码],我们继续简化一下源码如下所示 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
internal
class
DefaultControllerActivator : IControllerActivator
{
private
readonly
ITypeActivatorCache _typeActivatorCache;
public
DefaultControllerActivator(ITypeActivatorCache typeActivatorCache)
{
_typeActivatorCache = typeActivatorCache;
}
/// <summary>
/// Controller实例的创建方法
/// </summary>
public
object
Create(ControllerContext controllerContext)
{
//获取Controller类型信息
var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo;
//获取ServiceProvider
var serviceProvider = controllerContext.HttpContext.RequestServices;
//创建controller实例
return
_typeActivatorCache.CreateInstance<
object
>(serviceProvider, controllerTypeInfo.AsType());
}
/// <summary>
/// 释放Controller实例
/// </summary>
public
void
Release(ControllerContext context,
object
controller)
{
//如果controller实现了IDisposable接口,那么Release的时候会自动调用Controller的Dispose方法
//如果我们在Controller中存在需要释放或者关闭的操作,可以再Controller的Dispose方法中统一释放
if
(controller
is
IDisposable disposable)
{
disposable.Dispose();
}
}
}
|
通过上面的代码我们依然要继续深入到ITypeActivatorCache实现中去寻找答案,通过查看MvcCoreServiceCollectionExtensions类的AddMvcCoreServices方法源码我们可以找到如下信息 。
1
|
services.TryAddSingleton<ITypeActivatorCache, TypeActivatorCache>();
|
有了这个信息,我们可以直接找到TypeActivatorCache类的源码[点击查看源码]代码并不多,大致如下所示 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
internal
class
TypeActivatorCache : ITypeActivatorCache
{
//创建ObjectFactory的委托
private
readonly
Func<Type, ObjectFactory> _createFactory =
(type) => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);
//Controller类型和对应创建Controller实例的ObjectFactory实例的缓存
private
readonly
ConcurrentDictionary<Type, ObjectFactory> _typeActivatorCache =
new
ConcurrentDictionary<Type, ObjectFactory>();
/// <summary>
/// 真正创建实例的地方
/// </summary>
public
TInstance CreateInstance<TInstance>(
IServiceProvider serviceProvider,
Type implementationType)
{
//真正创建的操作是createFactory
//通过Controller类型在ConcurrentDictionary缓存中获得ObjectFactory
//而ObjectFactory实例由ActivatorUtilities.CreateFactory方法创建的
var createFactory = _typeActivatorCache.GetOrAdd(implementationType, _createFactory);
//返回创建实例
return
(TInstance)createFactory(serviceProvider, arguments:
null
);
}
}
|
通过上面类的代码我们可以清晰的得出一个结论,默认情况下Controller实例是由ObjectFactory创建出来的,而ObjectFactory实例是由ActivatorUtilities的CreateFactory创建出来,所以Controller实例每次都是由ObjectFactory创建而来,并非注册到IOC容器中。并且我们还可以得到一个结论ObjectFactory应该是一个委托,我们找到ObjectFactory定义的地方[点击查看源码] 。
1
|
delegate
object
ObjectFactory(IServiceProvider serviceProvider,
object
[] arguments);
|
这个确实如我们猜想的那般,这个委托会通过IServiceProvider实例去构建类型的实例,通过上述源码相关的描述我们会产生一个疑问,既然Controller实例并非由IOC容器托管,它由ObjectFactory创建而来,但是ObjectFactory实例又是由ActivatorUtilities构建的,那么生产对象的核心也就在ActivatorUtilities类中,接下来我们就来探究一下ActivatorUtilities的神秘面纱.
书接上面,我们知道了ActivatorUtilities类是创建Controller实例最底层的地方,那么ActivatorUtilities到底和容器是啥关系,因为我们看到了ActivatorUtilities创建实例需要依赖ServiceProvider,一切都要从找到ActivatorUtilities类的源码开始。我们最初接触这个类的地方在于它通过CreateFactory方法创建了ObjectFactory实例,那么我们就从这个地方开始,找到源码位置[点击查看源码]实现如下 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
static
ObjectFactory CreateFactory(Type instanceType, Type[] argumentTypes)
{
//查找instanceType的构造函数
//找到构造信息ConstructorInfo
//得到给定类型与查找类型instanceType构造函数的映射关系
FindApplicableConstructor(instanceType, argumentTypes,
out
ConstructorInfo constructor,
out
int
?[] parameterMap);
//构建IServiceProvider类型参数
var provider = Expression.Parameter(
typeof
(IServiceProvider),
"provider"
);
//构建给定类型参数数组参数
var argumentArray = Expression.Parameter(
typeof
(
object
[]),
"argumentArray"
);
//通过构造信息、构造参数对应关系、容器和给定类型构建表达式树Body
var factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
//构建lambda
var factoryLamda = Expression.Lambda<Func<IServiceProvider,
object
[],
object
>>(
factoryExpressionBody, provider, argumentArray);
var result = factoryLamda.Compile();
//返回执行结果
return
result.Invoke;
}
|
ActivatorUtilities类的CreateFactory方法代码虽然比较简单,但是它涉及到调用了其他方法,由于嵌套的比较深代码比较多,而且不是本文讲述的重点,我们就不再这里细说了,我们可以大概的描述一下它的工作流程.
综上述的相关步骤,我们可以得到一个结论,Controller实例的初始化是通过遍历Controller类型构造函数里的参数,然后根据构造函数每个参数的类型在IServiceProvider查找已经注册到容器中相关的类型实例,最终初始化得到的Controller实例。这就是在IServiceProvider得到需要的依赖关系,然后创建自己的实例,它内部是使用的表达式树来完成的这一切,可以理解为更高效的反射方式.
关于ActivatorUtilities类还包含了其他比较实用的方法,比如CreateInstance方法 。
1
|
public
static
T CreateInstance<T>(IServiceProvider provider,
params
object
[] parameters)
|
它可以通过构造注入的方式创建指定类型T的实例,其中构造函数里具体的参数实例是通过在IServiceProvider实例里获取到的,比如我们我们有这么一个类 。
1
2
3
4
5
6
7
8
9
10
11
|
public
class
OrderController
{
private
readonly
IOrderService _orderService;
private
readonly
IPersonService _personService;
public
OrderController(IOrderService orderService, IPersonService personService)
{
_orderService = orderService;
_personService = personService;
}
}
|
其中它所依赖的IOrderService和IPersonService实例是注册到IOC容器中的 。
1
2
3
|
IServiceCollection services =
new
ServiceCollection()
.AddScoped<IPersonService, PersonService>()
.AddScoped<IOrderService, OrderService>();
|
然后你想获取到OrderController的实例,但是它只包含一个有参构造函数,但是构造函数的参数都以注册到IOC容器中。当存在这种场景你便可以通过以下方式得到你想要的类型实例,如下所示 。
1
2
|
IServiceProvider serviceProvider = services.BuildServiceProvider();
OrderController orderController = ActivatorUtilities.CreateInstance<OrderController>(serviceProvider);
|
即使你的类型OrderController并没有注册到IOC容器中,但是它的依赖都在容器中,你也可以通过构造注入的方式得到你想要的实例。总的来说ActivatorUtilities里的方法还是比较实用的,有兴趣的同学可以自行尝试一下,也可以通过查看ActivatorUtilities源码的方式了解它的工作原理.
上面我们主要是讲解了默认情况下Controller并不是托管到IOC容器中的,它只是表现出来的让你以为它是在IOC容器中,因为它可以通过构造函数注入相关实例,这主要是ActivatorUtilities类的功劳。说了这么多Controller实例到底可不可以注册到IOC容器中,让它成为真正受到IOC容器的托管者。要解决这个,必须要满足两点条件 。
如果我们自己去实现将Controller托管到IOC容器中,就需要满足以上两个操作一个是要将Controller放入容器,然后让创建Controller的地方从IOC容器中直接获取Controller实例。庆幸的是,微软帮我们封装了一个相关的方法,它可以帮我们解决将Controller托管到IOC容器的问题,它的使用方法如下所示 。
1
2
3
|
services.AddMvc().AddControllersAsServices();
//或其他方式,这取决于你构建的Web项目的用途可以是WebApi、Mvc、RazorPage等
//services.AddMvcCore().AddControllersAsServices();
|
相信大家都看到了,玄机就在AddControllersAsServices方法中,但是它存在于MvcCoreMvcBuilderExtensions类和MvcCoreMvcCoreBuilderExtensions类中,不过问题不大,因为它们的代码是完全一样的。只是因为你可以通过多种方式构建Web项目比如AddMvc或者AddMvcCore,废话不多说直接上代码[点击查看源码] 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
static
IMvcBuilder AddControllersAsServices(
this
IMvcBuilder builder)
{
if
(builder ==
null
)
{
throw
new
ArgumentNullException(nameof(builder));
}
var feature =
new
ControllerFeature();
builder.PartManager.PopulateFeature(feature);
//第一将Controller实例添加到IOC容器中
foreach
(var controller
in
feature.Controllers.Select(c => c.AsType()))
{
//注册的声明周期是Transient
builder.Services.TryAddTransient(controller, controller);
}
//第二替换掉原本DefaultControllerActivator的为ServiceBasedControllerActivator
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
return
builder;
}
|
第一点没问题那就是将Controller实例添加到IOC容器中,第二点它替换掉了DefaultControllerActivator为为ServiceBasedControllerActivator。通过上面我们讲述的源码了解到DefaultControllerActivator是默认提供Controller实例的地方是获取Controller实例的核心所在,那么我们看看ServiceBasedControllerActivator与DefaultControllerActivator到底有何不同,直接贴出代码[点击查看源码] 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
ServiceBasedControllerActivator : IControllerActivator
{
public
object
Create(ControllerContext actionContext)
{
if
(actionContext ==
null
)
{
throw
new
ArgumentNullException(nameof(actionContext));
}
//获取Controller类型
var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();
//通过Controller类型在容器中获取实例
return
actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
}
public
virtual
void
Release(ControllerContext context,
object
controller)
{
}
}
|
相信大家对上面的代码一目了然了,和我们上面描述的一样,将创建Controller实例的地方改造了在容器中获取的方式。不知道大家有没有注意到ServiceBasedControllerActivator的Release的方法居然没有实现,这并不是我没有粘贴出来,确实是没有代码,之前我们看到的DefaultControllerActivator可是有调用Controller的Disposed的方法,这里却啥也没有。相信聪明的你已经想到了,因为Controller已经托管到了IOC容器中,所以他的生命及其相关释放都是由IOC容器完成的,所以这里不需要任何操作.
我们上面还看到了注册Controller实例的时候使用的是TryAddTransient方法,也就是说每次都会创建Controller实例,至于为什么,我想大概是因为每次请求都其实只会需要一个Controller实例,况且EFCore的注册方式官方建议也是Scope的,而这里的Scope正是对应的一次Controller请求。在加上自带的IOC会提升依赖类型的声明周期,如果将Controller注册为单例的话如果使用了EFCore那么它也会被提升为单例,这样会存在很大的问题。也许正是基于这个原因默认才将Controller注册为Transient类型的,当然这并不代表只能注册为Transient类型的,如果你不使用类似EFCore这种需要作用域为Scope的服务的时候,而且保证使用的主键都可以使用单例的话,完全可以将Controller注册为别的生命周期,当然这种方式个人不是很建议.
有时候大家可能会结合Autofac一起使用,Autofac确实是一款非常优秀的IOC框架,它它支持属性和构造两种方式注入,关于Autofac托管自带IOC的原理咱们在之前的文章浅谈.Net Core DependencyInjection源码探究中曾详细的讲解过,这里咱们就不过多的描述了,咱们今天要说的是Autofac和Controller的结合。如果你想保持和原有的IOC一致的使用习惯,即只使用构造注入的话,你只需要完成两步即可 。
首先将默认的IOC容器替换为Autofac,具体操作也非常简单,如下所示 。
1
2
3
4
5
6
7
8
|
public
static
IHostBuilder CreateHostBuilder(
string
[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
//只需要在这里设置ServiceProviderFactory为AutofacServiceProviderFactory即可
.UseServiceProviderFactory(
new
AutofacServiceProviderFactory());
|
然后就是咱们之前说的,要将Controller放入容器中,然后修改生产Controller实例的ControllerFactory的操作为在容器中获取,当然这一步微软已经为我们封装了便捷的方法 。
1
|
services.AddMvc().AddControllersAsServices();
|
只需要通过上面简单得两步,既可以将Controller托管到Autofac容器中。但是,我们说过了Autofac还支持属性注入,但是默认的方式只支持构造注入的方式,那么怎么让Controller支持属性注入呢?我们还得从最根本的出发,那就是解决Controller实例存和取的问题 。
首先为了让Controller托管到Autofac中并且支持属性注入,那么就只能使用Autofac的方式去注册Controller实例,具体操作是在Startup类中添加ConfigureContainer方法,然后注册Controller并声明支持属性注入 。
1
2
3
4
5
6
7
8
9
|
public
void
ConfigureContainer(ContainerBuilder builder)
{
var controllerBaseType =
typeof
(ControllerBase);
//扫描Controller类
builder.RegisterAssemblyTypes(
typeof
(Program).Assembly)
.Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
//属性注入
.PropertiesAutowired();
}
|
其次是解决取的问题,这里我们就不需要AddControllersAsServices方法了,因为AddControllersAsServices解决了Controller实例在IOC中存和取的问题,但是这里我们只需要解决Controller取得问题说只需要使用ServiceBasedControllerActivator即可,具体操作是 。
1
|
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
|
仅需要在默认的状态下完成这两步,既可以解决Controller托管到Autofac中并支持属性注入的问题,这也是最合理的方式。当然如果你使用AddControllersAsServices可是可以实现相同的效果了,只不过是没必要将容器重复的放入容器中了.
本文我们讲述了关于ASP.NET Core Controller与IOC结合的问题,我觉得这是有必要让每个人都有所了解的知识点,因为在日常的Web开发中Controller太常用了,知道这个问题可能会让大家在开发中少走一点弯路,接下来我们来总结一下本文大致讲解的内容 。
到此这篇关于ASP.NET Core Controller与IOC结合问题整理的文章就介绍到这了,更多相关ASP.NET Core Controller与IOC结合问题内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/wucy/p/14222973.html 。
最后此篇关于ASP.NET Core Controller与IOC结合问题整理的文章就讲到这里了,如果你想了解更多关于ASP.NET Core Controller与IOC结合问题整理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
主要思想是将 EF Core nuget 包添加到 .NET Core 库项目,然后在一堆应用程序(例如 ASP.NET Core、Win 服务、控制台应用程序)中使用该库,而无需在每个应用程序中配置
我想要实现的是编写一个简单的.net核心后台工作程序(.net core 3.1)的代码,在该工作程序作为Windows服务运行时,我在其中将数据写入SQL Server数据库(通过EF Core 3
关于 .Net Core SDK download page 二进制文件有什么用?它与安装程序有何不同? 最佳答案 二进制文件是 .NET Core 的编译代码。它们拥有运行 .NET Core 所需
.NET Core 和 Entity Framework Core 之间的区别?我们可以在 .NET Core 中使用 Entity Framework Core 吗?两者都有什么优势? 最佳答案 E
.NET Core 和 ASP.NET Core 到底有什么区别? 它们是相互排斥的吗?我听说 ASP.NET Core 是基于 .NET Core 构建的,但它也可以基于完整的 .NET 框架构建。
我对 ASP.NET Core 开发完全陌生。我正在尝试使用单个模型和 mysql 创建一个简单的 asp.net core Web api 来存储模型数据,然后我想使用 Swagger 将其作为 R
.NET Core 和 Entity Framework Core 之间的区别?我们可以在 .NET Core 中使用 Entity Framework Core 吗?两者都有什么优势? 最佳答案 E
好吧,作为一个新的 .net 开发生态系统,我有点迷失在核心工具、版本等方面。 有人可以解释我之间的区别吗 VS 2015 核心工具预览版 x - See here .NET Core/SDK 与否
我已阅读有关如何通过信号器核心集线器从后台服务向客户端发送通知的文档。如何从客户端接收到后台服务的通知? 后台服务应该只是一个单例。 public class Startup { public
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 4年前关闭。 Improve t
非常简单的问题: 我正在尝试创建一个像这样的谓词构建器: var predicate = PredicateBuilder.False(); 但似乎在Net Core和EF Core中不可用。
在 .NET Core 自包含应用程序 中...我们需要在 project.json 中指定运行时 (RID) 我们希望我们的应用程序针对...发布为什么会这样? .NET Core 是跨平台的,与我
如何用 iCloud Core Data 替换我现有的 Core Data?这是我的持久商店协调员: lazy var persistentStoreCoordinator: NSPersistent
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 2 年前。 Improv
今天我正在学习新的 ASP.net 核心 API 3.1,我想将我的旧网站从 MVC4 转移到 Web API。除了一件事,一切都很好。数据库连接。在我的旧网站中,我为每个客户端(10/15 数据库)
我在 Visual Studio 2015 Update 3 和 .NET Core 1.0 中工作。我有一个 .NETCoreApp v1.0 类型的 Web API 项目。当我添加一个 .NET
我一直在尝试遵循 Ben Cull ( http://benjii.me/2016/06/entity-framework-core-migrations-for-class-library-proj
当我打开我的 vs 代码程序时,我收到以下消息: 无法找到 .NET Core SDK。 .NET Core 调试将不会启用。确保 .NET Core SDK 已安装并且在路径上。 如果我安装甚至卸载
我偶然发现了一个非常奇怪的问题。每当 Web 应用程序启动时,dotnet.exe 都会使用相当多的内存(大约 300M)。然而,当它触及某些部分时(我感觉这与 EF Core 使用有关),它会在短时
ASP.NET Core Web (.NET Core) 与 ASP.NET Core Web (.NET Framework) 有什么区别? .NET Framework 是否提供 similar
我是一名优秀的程序员,十分优秀!