首页 > 编程语言 >Asp.net core mapcontrollers 背后干了些啥

Asp.net core mapcontrollers 背后干了些啥

时间:2023-03-05 22:26:00浏览次数:61  
标签:core Asp return action State var net endpoints ref

1.背景

当我们在写webapi的时候我们发现,框架自动帮我们写好了 app.MapControllers(),看注释写的是帮我们将controllerl里面的action映射为我们的终结点,那具体是怎么弄得呢,我觉得可以仔细研究一下,看一下背后的逻辑.

2.开始研究,用dnspy看一下源码

public static ControllerActionEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints)
		{
    		//确保依赖的服务已经注册 可以进去看看帮我们注入了哪些东西,后面的代码可以看到它们身影
			ControllerEndpointRouteBuilderExtensions.EnsureControllerServices(endpoints);
			return ControllerEndpointRouteBuilderExtensions.GetOrCreateDataSource(endpoints).DefaultBuilder;
		}
//接上面,创建endpoint数据源
private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
		{
			ControllerActionEndpointDataSource controllerActionEndpointDataSource = endpoints.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault<ControllerActionEndpointDataSource>();
			if (controllerActionEndpointDataSource == null)
			{
                //相关服务
				OrderedEndpointsSequenceProviderCache requiredService = endpoints.ServiceProvider.GetRequiredService<OrderedEndpointsSequenceProviderCache>();
                //获取数据源
				controllerActionEndpointDataSource = endpoints.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSourceFactory>().Create(requiredService.GetOrCreateOrderedEndpointsSequenceProvider(endpoints));
				endpoints.DataSources.Add(controllerActionEndpointDataSource);
			}
			return controllerActionEndpointDataSource;
		}
		

重点就是 ControllerActionEndpointDataSourceFactory.Create() 函数帮我们构建数据源。

controllerActionEndpointDataSourceFactory

internal class ControllerActionEndpointDataSourceFactory
    {
        private readonly ControllerActionEndpointDataSourceIdProvider _dataSourceIdProvider;
        private readonly IActionDescriptorCollectionProvider _actions;
        private readonly ActionEndpointFactory _factory;

        public ControllerActionEndpointDataSourceFactory(
            ControllerActionEndpointDataSourceIdProvider dataSourceIdProvider,
            //这个很重要,为我们提供controller action的信息,具体实现类应该是
            //ActionDescriptorCollectionProvider
            IActionDescriptorCollectionProvider actions,
            ActionEndpointFactory factory)
        {
            _dataSourceIdProvider = dataSourceIdProvider;
            _actions = actions;
            _factory = factory;
        }

        public ControllerActionEndpointDataSource Create(OrderedEndpointsSequenceProvider orderProvider)
        {   
            //直接new一个 datasource
            return new ControllerActionEndpointDataSource(_dataSourceIdProvider, _actions, _factory, orderProvider);
        }
    }

终于看到我们的datascource了,所以进去自己看看,如果构建的endpoint.以及最终实现的类是哪个。直接点进去类,可以看到类的构造函数(没贴出来),实际上当我们第一次启动应用的时候,endpoint是没有构建出来的,直到第一次访问,才会进行初始化. 可以看到调用链 Initialize() =>UpdateEndpoints()=>CreateEndpoints()

internal abstract class ActionEndpointDataSourceBase : EndpointDataSource, IDisposable
{
     //获取endpoints
     public override IReadOnlyList<Endpoint> Endpoints
        {
            get
            {
                Initialize();            
                return _endpoints;
            }
        }
    private void Initialize()
        {
            if (_endpoints == null)
            {
                lock (Lock)
                {
                    if (_endpoints == null)
                    {
                        UpdateEndpoints();
                    }
                }
            }
        }

        private void UpdateEndpoints()
        {
            lock (Lock)
            {
                var endpoints = CreateEndpoints(_actions.ActionDescriptors.Items, Conventions);
                // See comments in DefaultActionDescriptorCollectionProvider. These steps are done
                // in a specific order to ensure callers always see a consistent state.
                // Step 1 - capture old token
                var oldCancellationTokenSource = _cancellationTokenSource;
                // Step 2 - update endpoints
                _endpoints = endpoints;
                // Step 3 - create new change token
                _cancellationTokenSource = new CancellationTokenSource();
                _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token);
                // Step 4 - trigger old token
                oldCancellationTokenSource?.Cancel();
            }
        }
}
internal class ControllerActionEndpointDataSource:ActionEndpointDataSourceBase
{
     protected override List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions)
        {
            var endpoints = new List<Endpoint>();
            var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            // MVC guarantees that when two of it's endpoints have the same route name they are 			   //equivalent.
            // However, Endpoint Routing requires Endpoint Names to be unique.
            var routeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            // For each controller action - add the relevant endpoints.
            // 1. If the action is attribute routed, we use that information verbatim
            // 2. If the action is conventional routed
            //      a. Create a *matching only* endpoint for each action X route (if possible)
            //      b. Ignore link generation for now
            for (var i = 0; i < actions.Count; i++)
            {
                if (actions[i] is ControllerActionDescriptor action)
                {
                    //逻辑重点,为每个action构建一个endpoint.
                    _endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);
                    if (_routes.Count > 0)
                    {
                        // If we have conventional routes, keep track of the keys so we can create
                        // the link generation routes later.
                        foreach (var kvp in action.RouteValues)
                        {
                            keys.Add(kvp.Key);
                        }
                    }
                }
            }
            // Now create a *link generation only* endpoint for each route. This gives us a very
            // compatible experience to previous versions.
            for (var i = 0; i < _routes.Count; i++)
            {
                var route = _routes[i];
                _endpointFactory.AddConventionalLinkGenerationRoute(endpoints, routeNames, keys, route, conventions);
            }
            return endpoints;
        }
}

可以看到我们针对于每个action构建一个endpoint,AddEndpoints()所以继续进去看看逻辑。

public void AddEndpoints( List<Endpoint> endpoints, HashSet<string> routeNames,ActionDescriptor action,IReadOnlyList<ConventionalRouteEntry> routes,IReadOnlyList<Action<EndpointBuilder>> conventions, bool createInertEndpoints)
{
    //不走的逻辑 代码被我省略删除 路由模板不为空。
    if (action.AttributeRouteInfo?.Template != null){
        
        //构建出来处理去请求的委托.很重要
        var requestDelegate = CreateRequestDelegate(action) ?? _requestDelegate;
        		//属性路由模板
                var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);
                // Modify the route and required values to ensure required values can be successfully subsituted.
                // Subsitituting required values into an attribute route pattern should always succeed.
                var (resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern);
                var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues);
               //省略不重要代码
                var builder = new RouteEndpointBuilder(requestDelegate, updatedRoutePattern, action.AttributeRouteInfo.Order)
                {
                    DisplayName = action.DisplayName,
                };//添加action data到builder
                AddActionDataToBuilder(
                    builder,
                    routeNames,
                    action,
                    action.AttributeRouteInfo.Name,
                    dataTokens: null,
                    action.AttributeRouteInfo.SuppressLinkGeneration,
                    action.AttributeRouteInfo.SuppressPathMatching,
                    conventions,
                    perRouteConventions: Array.Empty<Action<EndpointBuilder>>());
                endpoints.Add(builder.Build());
    }
}

CreateRequestDelegate可以看到构建处理器的逻辑,我们进去看看,由于是ioc注入的容器所以我们需要找到实现类。实现类是 ControllerRequestDelegateFactory类

ControllerRequestDelegateFactory

private RequestDelegate? CreateRequestDelegate(ActionDescriptor action, RouteValueDictionary? dataTokens = null)
        {
            foreach (var factory in _requestDelegateFactories)
            {	//看来我们可以注入我们的处理器逻辑,需要注明一下顺序(或者是我记得后来注入的在最前面?)
                var requestDelegate = factory.CreateRequestDelegate(action, dataTokens);
                if (requestDelegate != null)
                {
                    return requestDelegate;
                }
            }
            return null;
        }
// ControllerRequestDelegateFactory方法
public RequestDelegate? CreateRequestDelegate(ActionDescriptor actionDescriptor, RouteValueDictionary? dataTokens)
        {	 //省略不重要代码
			//针对于 context的委托.非常完美
            return context =>
            {
                RouteData routeData;
                if (dataTokens is null or { Count: 0 })
                {
                    routeData = new RouteData(context.Request.RouteValues);
                }
                else
                {
                    routeData = new RouteData();
                    routeData.PushState(router: null, context.Request.RouteValues, dataTokens);
                }
                var actionContext = new ActionContext(context, routeData, actionDescriptor);
                var controllerContext = new ControllerContext(actionContext)
                {
                    // PERF: These are rarely going to be changed, so let's go copy-on-write.
                    ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>	(_valueProviderFactories)
                };
                controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;
			   //实际上这点很重要,这里是帮助我们构建controller类的,因为控制器类没有注入ioc容器
                var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext);
                //很重要的一个东西,直接把我们引入到mvc的世界中,各种filter逻辑都在这
                var invoker = new ControllerActionInvoker(
                    _logger,
                    _diagnosticListener,
                    _actionContextAccessor,
                    _mapper,
                    controllerContext,
                    cacheEntry,
                    filters);
                return invoker.InvokeAsync();
            };
        }

看了半天找到了构建controller类的逻辑,那我们进去看看,既然没有注入到ioc,那么是如何构建的呢.进去getcacheResult中看看。

Controller类的生成逻辑

public (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext)
        {
            var actionDescriptor = controllerContext.ActionDescriptor;  
            // We don't care about thread safety here
            if (cacheEntry is null)
            {
                //省略 部分代码
                //这个是重点,实际上是一个委托,为我们生成controller,下面有代码
                var controllerFactory =_controllerFactoryProvider.CreateControllerFactory(actionDescriptor);
                var controllerReleaser = _controllerFactoryProvider.CreateAsyncControllerReleaser(actionDescriptor);
                // 省略 部分代码
                cacheEntry = new ControllerActionInvokerCacheEntry(
                    filterFactoryResult.CacheableFilters,
                    controllerFactory,
                    controllerReleaser,
                    propertyBinderFactory,
                    objectMethodExecutor,
                    actionMethodExecutor);
                actionDescriptor.CacheEntry = cacheEntry;
            }
    		// 省略 ......
            return (cacheEntry, filters);
        }

逻辑是一个provider构建委托生成的,注入的实现类是 ControllerFactoryProvider 看看逻辑

ControllerFactoryProvider

public Func<ControllerContext, object> CreateControllerFactory(ControllerActionDescriptor descriptor)
        {
            //省略 代码
            var controllerType = descriptor.ControllerTypeInfo?.AsType();
            if (_factoryCreateController != null)
            {   //第一种逻辑
                return _factoryCreateController;
            }
            var controllerActivator = _activatorProvider.CreateActivator(descriptor);
            var propertyActivators = GetPropertiesToActivate(descriptor);
    		//第二种逻辑
            object CreateController(ControllerContext controllerContext)
            {
                var controller = controllerActivator(controllerContext);
                for (var i = 0; i < propertyActivators.Length; i++)
                {
                    var propertyActivator = propertyActivators[i];
                    propertyActivator(controllerContext, controller);
                }
                return controller;
            }
            return CreateController;
        }

返回生成controller类的委托有两种逻辑 ,实际上我们走的是第一种 即 _factoryCreateController ,它在构造的时候被初始化为 _factoryCreateController = controllerFactory.CreateController;controllerFactory类又是哪个类呢,实际上是我们注入的DefaultControllerFactory 类,很熟悉把,因为以前的版本也是这么做的

DefaultControllerFactory

public object CreateController(ControllerContext context)
        {
         	//重点代码
            var controller = _controllerActivator.Create(context);
            foreach (var propertyActivator in _propertyActivators)
            {
                propertyActivator.Activate(context, controller);
            }

            return controller;
        }

根据你的一个controllercontext生成我们的控制类,_controllerActivator的实现类实际上我们注入的是 DefaultControllerActivator,进去具体看看

DefaultControllerActivator

 public object Create(ControllerContext controllerContext)
        {
            //核心代码 删除了一些代码
            var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo;
            var serviceProvider = controllerContext.HttpContext.RequestServices;
            return _typeActivatorCache.CreateInstance<object>(serviceProvider, controllerTypeInfo.AsType());
        }

引出了新的类 _typeActivatorCache ,这个类的注入实际上是 TypeActivatorCache

TypeActivatorCache

internal class TypeActivatorCache : ITypeActivatorCache
    {
        private readonly Func<Type, ObjectFactory> _createFactory =
            (type) => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);
        private readonly ConcurrentDictionary<Type, ObjectFactory> _typeActivatorCache =
               new ConcurrentDictionary<Type, ObjectFactory>();

        /// <inheritdoc/>
        public TInstance CreateInstance<TInstance>(
            IServiceProvider serviceProvider,
            Type implementationType)
        {
            //核心代码 其他代码删除
            var createFactory = _typeActivatorCache.GetOrAdd(implementationType, _createFactory);
            return (TInstance)createFactory(serviceProvider, arguments: null);
        }
    }

那么生成controller的大佬终于出来了 ActivatorUtilities 是这个类 ,名字听着就霸气,进一步进去 结果看不到了,因为asp.net core的源码没有这部分。所以就在这停下吧 .如果你想更深入的了解不妨看看这篇文章,深入且详细 ASP.NET Core Controller与IOC的羁绊

controller类的生成总结

它不是托管到ioc的,而是通过其他的方式获取 ,获取逻辑:ControllerActionInvokerCache.GetCachedResult()=>ControllerFactoryProvider.CreateControllerFactory()=>DefaultControllerFactory.CreateController()=>DefaultControllerActivator.Create()=>TypeActivatorCache.CreateInstance()=>ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);这个就是一步步的调用逻辑,不像我写逻辑,直接new一个完事

controller类的调用逻辑

上面知道了 controller源头从哪来,那现在就要寻找他的出生地。继续找逻辑.我们知道CreateRequestDelegate生成处理委托,这就是处理整个处理逻辑,返回了一个ControllerActionInvoker 。

 var invoker = new ControllerActionInvoker(
                    _logger,
                    _diagnosticListener,
                    _actionContextAccessor,
                    _mapper,
                    controllerContext,
                    cacheEntry,
                    filters);

 return invoker.InvokeAsync();

实际上是调用 InvokeAsync().这个就吊了,逻辑就涉及到我们的各种过滤器了.

ResourceInvoker

public virtual Task InvokeAsync()
        {
            _actionContextAccessor.ActionContext = _actionContext;
            var scope = _logger.ActionScope(_actionContext.ActionDescriptor);
            Task task;
            try
            {  // 调用filter管道
                task = InvokeFilterPipelineAsync();
            }
            catch (Exception exception)
            {
                return Awaited(this, Task.FromException(exception), scope);
            }

            if (!task.IsCompletedSuccessfully)
            {
			  return Awaited(this, task, scope);
            }
            static async Task Awaited(ResourceInvoker invoker, Task task, IDisposable? scope)
            {
                try
                {
                    await task;
                }
                finally
                {
                    await invoker.ReleaseResourcesCore(scope);
                }
            }        
        }

InvokeFilterPipelineAsync

private Task InvokeFilterPipelineAsync()
        {
            var next = State.InvokeBegin;
            // The `scope` tells the `Next` method who the caller is, and what kind of state to initialize to
            // communicate a result. The outermost scope is `Scope.Invoker` and doesn't require any type
            // of context or result other than throwing.
            var scope = Scope.Invoker;
            // The `state` is used for internal state handling during transitions between states. In practice this
            // means storing a filter instance in `state` and then retrieving it in the next state.
            var state = (object?)null;
            // `isCompleted` will be set to true when we've reached a terminal state.
            var isCompleted = false;
            try
            {
                while (!isCompleted)
                {
                    var lastTask = Next(ref next, ref scope, ref state, ref isCompleted);
                    if (!lastTask.IsCompletedSuccessfully)
                    {
                        return Awaited(this, lastTask, next, scope, state, isCompleted);
                    }
                }

                return Task.CompletedTask;
            }
            catch (Exception ex)
            {
                // Wrap non task-wrapped exceptions in a Task,
                // as this isn't done automatically since the method is not async.
                return Task.FromException(ex);
            }
            static async Task Awaited(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object? state, bool isCompleted)
            {
                await lastTask;
                while (!isCompleted)
                {
                    await invoker.Next(ref next, ref scope, ref state, ref isCompleted);
                }
            }
        }

结合 next函数实用 (贴部分代码)

next函数

private Task Next(ref State next, ref Scope scope, ref object? state, ref bool isCompleted)
        {
            switch (next)
            {
                case State.InvokeBegin:
                    {
                        goto case State.AuthorizationBegin;
                    }
                case State.AuthorizationBegin:
                    {
                        _cursor.Reset();
                        goto case State.AuthorizationNext;
                    }
				//删除了一堆代码
                case State.ActionBegin:
                    {   
                        //实际上调用此代码
                        var task = InvokeInnerFilterAsync();
                        if (!task.IsCompletedSuccessfully)
                        {
                            next = State.ActionEnd;
                            return task;
                        }
                        goto case State.ActionEnd;
                    }
                case State.ActionEnd:
                    {
                        if (scope == Scope.Exception)
                        {
                            // If we're inside an exception filter, let's allow those filters to 'unwind' before
                            // the result.
                            isCompleted = true;
                            return Task.CompletedTask;
                        }
                        var task = InvokeResultFilters();
                        if (!task.IsCompletedSuccessfully)
                        {
                            next = State.ResourceInsideEnd;
                            return task;
                        }
                        goto case State.ResourceInsideEnd;
                    }           
                case State.InvokeEnd:
                    {
                        isCompleted = true;
                        return Task.CompletedTask;
                    }
                default:
                    throw new InvalidOperationException();
            }
        }

可以看到上面的调用逻辑,实际上是状态机,指明下一步调用的函数和逻辑,然后直到完全完成任务,我们可以看到 当actionbegin开始的时候调用的任务是 InvokeInnerFilterAsync,可以点进去具体看看,具体类是ControllerActionInvoker

ControllerActionInvoker

protected override Task InvokeInnerFilterAsync()
    {
        try
        {
            var next = State.ActionBegin;
            var scope = Scope.Invoker;
            var state = (object?)null;
            var isCompleted = false;
            while (!isCompleted)
            {
                //重点代码 
                var lastTask = Next(ref next, ref scope, ref state, ref isCompleted);
                if (!lastTask.IsCompletedSuccessfully)
                {
                    //返回task ,实际保证了内部逻辑执行的有序性。
                    return Awaited(this, lastTask, next, scope, state, isCompleted);
                }
            }
            return Task.CompletedTask;
        } 
        static async Task Awaited(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, object? state, bool isCompleted)
        { //参数都是枚举,值类型 或者结构 这点很重要.
            await lastTask;
            while (!isCompleted)
            {
                await invoker.Next(ref next, ref scope, ref state, ref isCompleted);
            }
        }
    }
private Task Next(ref State next, ref Scope scope, ref object? state, ref bool isCompleted)
        {
            switch (next)
            {
                case State.ActionBegin:
                    {
                        var controllerContext = _controllerContext;
					 //游标指针归0,指向当前的filter位置.
                        _cursor.Reset();
					  //调用了 委托生成 controller实例. 生成controller
                        _instance = _cacheEntry.ControllerFactory(controllerContext);
                        var task = BindArgumentsAsync();
                        if (task.Status != TaskStatus.RanToCompletion)
                        {
                            next = State.ActionNext;
                            return task;
                        }
                        goto case State.ActionNext;
                    }
                  // 如果filter调用完了,那么就进去到这个方法.
                 case State.ActionInside:
                    {
                        //调用controller的action方法.
                        var task = InvokeActionMethodAsync();
                        if (task.Status != TaskStatus.RanToCompletion)
                        {
                            next = State.ActionEnd;
                            return task;
                        }
                        goto case State.ActionEnd;
                    }
            }
    // 调用的具体方法
    private Task InvokeActionMethodAsync()
        {
            // 保留了一些重要代码.
            var objectMethodExecutor = _cacheEntry.ObjectMethodExecutor;
            var actionMethodExecutor = _cacheEntry.ActionMethodExecutor;
            var orderedArguments = PrepareArguments(_arguments, objectMethodExecutor);
		   //调用 controller action方法.
            var actionResultValueTask = actionMethodExecutor.Execute(_mapper, objectMethodExecutor, _instance!, orderedArguments);
            if (actionResultValueTask.IsCompletedSuccessfully)
            {
                _result = actionResultValueTask.Result;
            }
            else
            {
                return Awaited(this, actionResultValueTask);
            }
            return Task.CompletedTask;
        }

此时我们终于找到了 controller的调用逻辑链,同时我们也找到了 我们注册的过滤器在哪个位置被调用,它为我们揭开了神秘的面纱。invoker.InvokeAsync()=>InvokeFilterPipelineAsync()=>next() (状态机调用了我们注入的filter)=>InvokeActionMethodAsync()。

总结

做个总结,首先来说目标是想看看mapcontroller背后做了哪些事情,可以看到,它实际上给我们注册了endpoint数据源,通过endpointdatasourcefactory创建一个datasource(new 一个),实际上是我们为每一个action创建一个RouteEndpoint, 通过CreateRequestDelegate方法创建处理请求的委托,然后通过调用DefaultControllerFactory类为我们创建controller实例,并没有从ioc中获取,然后我们在我们在处理请求的时候,通过引入状态机的调用模式,将我们注入的filters来对请求进行先一步的处理,同时调用InvokeActionMethodAsync方法完成请求的处理。大概的逻辑就是这样。还有我们注入了endpointdatasource,所以才有userouting,useendpoint等进而注入一些重要的中间件,有兴趣的可以看看上一篇文章。
总结的总结,对着源码找了一下调用的逻辑,以后写bug的时候更加得心应手了.

有兴趣可以看看以下文章

ASP.NET Core Controller与IOC的羁绊
.NET Core开发日志——Controller

标签:core,Asp,return,action,State,var,net,endpoints,ref
From: https://www.cnblogs.com/guoxiaotian/p/17181705.html

相关文章

  • .net6 引用log4net记录日志
    第一步:nuget包引用使用log4net需要引用两个nuget包1.Log4net2.Microsoft.Extensions.Logging.Log4Net.AspNetCore第二步:引用log4net的config配置文件配置文件贴......
  • 如何编写Kubernetes的YAML(一)
    什么是API对象作为一个集群操作系统,Kubernetes归纳总结了Google多年的经验,在理论层面抽象出了很多个概念,用来描述系统的管理运维工作,这些概念就叫做“API对象”。因......
  • DotNet 5.0 部署 Docker 注意
    1.添加docker环境参数后会报错      日志显示  只好按照以下注释   2.Dockerfile问题  日志记载        日志显示......
  • C#/.net面试知识点总结【反射】(二)
    反射?通过反射调用对象要比直接NEW一个对象慢多 https://www.jb51.net/article/205935.htm动态获取程序集信息。程序集包含模块,而模块又包括类型,类型下有成员,反射就是管理......
  • Kubernetes(k8s)权限管理RBAC详解
    一、简介kubernetes集群相关所有的交互都通过apiserver来完成,对于这样集中式管理的系统来说,权限管理尤其重要,在1.5版的时候引入了RBAC(RoleBaseAccessControl)的权限......
  • C#/.net面试知识点总结(六)【垃圾回收器】
    托管代码可是享受CLR提供的服务(安全检测,垃圾回收,)不需要自己完成这些操作非托管代码需要自己提供安全检测,垃圾回收等操作 托管代码是一种中间语言,运行在CLR上。非托管代......
  • C#/.net 序列化与反序列化
    序列化就是将我们程序中的对象通过字节流写入存储媒体或网络流中。反序列化就是把已存入的媒体或接收的网络流中的内容转换成程序运行中的对象。这两个过程结合起来,可以轻......
  • .NET6 MVC 传值的五种方式
    //Controller:ViewBag.User1="张三";ViewData["User2"]="李四";TempData["User3"]="王五";HttpContext.Sessio......
  • 学习ASP.NET Core Blazor编程系列二十七——JWT登录(2)
    学习ASP.NETCoreBlazor编程系列文章之目录学习ASP.NETCoreBlazor编程系列一——综述学习ASP.NETCoreBlazor编程系列二——第一个Blazor应用程序(上)学习A......
  • #yyds干货盘点#【愚公系列】2023年03月 ASP.NET Core下Worker Service构建系统服务实
    前言当你想到ASP.NETCore时,可能会想到Web应用程序后端代码,包括MVC和WebAPI。MVC视图和Razor页面还允许使用后端代码生成带有HTML元素的前端UI。全新的Blazor更进一步,允许......