ASP.NET MVC Controller激活系统详解:IoC的应用[上篇]

简介:

所谓控制反转(IoC: Inversion Of Control)简单地说就是应用本身不负责依赖对象的创建和维护,而交给一个外部容器来负责。这样控制权就由应用转移到了外部IoC容器,控制权就实现了所谓的反转。比如在类型A中需要使用类型B的实例,而B实例的创建并不由A来负责,而是通过外部容器来创建。通过IoC的方式是实现针对目标Controller的激活具有重要的意义。

目录
一、从Unity来认识IoC
二、Controller与Model的分离
三、 创建基于IoC的自定义ControllerFactory
        实例演示:自定义一个基于Unity的ControllerFactory
四、ControllerActivator V.S. DependencyResoolver
五、通过自定义ControllerActivator实现IoC
六、通过自定义DependencyResoolver实现IoC

一、从Unity来认识IoC

有时我们又将IoC称为依赖注入(DI: Dependency Injection)。所谓依赖注入,就是由外部容器在运行时动态地将依赖的对象注入到组件之中。Martin Fowler在那篇著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中将具体依赖注入划分为三种形式,即构造器注入、属性(设置)注入和接口注入,而我个人习惯将其划分为一种(类型)匹配和三种注入:

  • 类型匹配(Type Matching):虽然我们通过接口(或者抽象类)来进行服务调用,但是服务本身还是实现在某个具体的服务类型中,这就需要某个类型注册机制来解决服务接口和服务类型之间的匹配关系;
  • 构造器注入(Constructor Injection):IoC容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;
  • 属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,IoC容器会自动初始化该属性;
  • 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,IoC容器会自动调用该方法。

开源社区具有很有流行的IoC框架,比如Castle Windsor、Unity、Spring.NET、StructureMap和Ninject等。Unity是微软Patterns & Practices部门开发的一个轻量级的IoC框架。该项目在Codeplex上的地址为http://unity.codeplex.com/, 你可以下载相应的安装包和开发文档。Unity的最新版本为2.1。出于篇幅的限制,我不可能对Unity进行前面的介绍,但是为了让读者了解IoC在Unity中的实现,我写了一个简单的程序。

我们创建一个控制台程序,定义如下几个接口(IA、IB、IC和ID)和它们各自的实现类(A、B、C、D)。在类型A中定义了3个属性B、C和D,其类型分别为接口IB、IC和ID。其中属性B在构在函数中被初始化,以为着它会以构造器注入的方式被初始化;属性C上应用了DependencyAttribute特性,意味着这是一个需要以属性注入方式被初始化的依赖属性;属性D则通过方法Initialize初始化,该方法上应用了特性InjectionMethodAttribute,意味着这是一个注入方法在A对象被IoC容器创建的时候会被自动调用。

   1: namespace UnityDemo
   2: {
   3:     public interface IA { }
   4:     public interface IB { }
   5:     public interface IC { }
   6:     public interface ID {}
   7:  
   8:     public class A : IA
   9:     {
  10:         public IB B { get; set; }
  11:         [Dependency]
  12:         public IC C { get; set; }
  13:         public ID D { get; set; }
  14:  
  15:         public A(IB b)
  16:         {
  17:             this.B = b;
  18:         }
  19:         [InjectionMethod]
  20:         public void Initialize(ID d)
  21:         {
  22:             this.D = d;
  23:         }
  24:     }
  25:     public class B: IB{}
  26:     public class C: IC{}
  27:     public class D: ID{}
  28: }

然后我们为该应用添加一个配置文件,并定义如下一段关于Unity的配置。这段配置定义了一个名称为defaultContainer的Unity容器,并在其中完成了上面定义的接口和对应实现类之间映射的类型匹配。

   1: <configuration>
   2:   <configSections>
   3:     <section name="unity" 
   4:          type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, 
   5:                Microsoft.Practices.Unity.Configuration"/>
   6:   </configSections>
   7:   <unity>
   8:     <containers>
   9:       <container name="defaultContainer">
  10:         <register type="UnityDemo.IA, UnityDemo" mapTo="UnityDemo.A, UnityDemo"/>
  11:         <register type="UnityDemo.IB, UnityDemo" mapTo="UnityDemo.B, UnityDemo"/>
  12:         <register type="UnityDemo.IC, UnityDemo" mapTo="UnityDemo.C, UnityDemo"/>
  13:         <register type="UnityDemo.ID, UnityDemo" mapTo="UnityDemo.D, UnityDemo"/>
  14:       </container>
  15:     </containers>
  16:   </unity> 
  17: </configuration>

最后在Main方法中创建一个代表IoC容器的UnityContainer对象,并加载配置信息对其进行初始化。然后调用它的泛型的Resolve方法创建一个实现了泛型接口IA的对象。最后将返回对象转变成类型A,并检验其B、C和D属性是否是空。

   1: static void Main(string[] args)
   2: {
   3:     IUnityContainer container = new UnityContainer();
   4:     UnityConfigurationSection configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName)
   5:         as UnityConfigurationSection;
   6:     configuration.Configure(container, "defaultContainer");
   7:     A a = container.Resolve<IA>() as A;
   8:     if (null != a)
   9:     {
  10:         Console.WriteLine("a.B == null ? {0}", a.B == null ? "Yes" : "No");
  11:         Console.WriteLine("a.C == null ? {0}", a.C == null ? "Yes" : "No");
  12:         Console.WriteLine("a.D == null ? {0}", a.D == null ? "Yes" : "No");
  13:     }
  14: }

从如下给出的执行结果我们可以得到这样的结论:通过Resolve<IA>方法返回的是一个类型为A的对象,该对象的三个属性被进行了有效的初始化。这个简单的程序分别体现了接口注入(通过相应的接口根据配置解析出相应的实现类型)、构造器注入(属性B)、属性注入(属性C)和方法注入(属性D)。[源代码从这里下载]

   1: a.B == null ? No
   2: a.C == null ? No
   3: a.D == null ? No

二、Controller与Model的分离

在《MVC、MVP以及Model2[下篇]》中我们谈到ASP.NET MVC是基于MVC的变体Model2设计的。ASP.NET MVC所谓的Model仅仅表示绑定到View上的数据,我们一般称之为View Model。而真正的Model一般意义上指维护应用状态和提供业务功能操作的领域模型,或者是针对业务层的入口或者业务服务的代理。真正的MVC在ASP.NET MVC中的体现如下图所示。

image

对于一个ASP.NET MVC应用来说,用户交互请求直接发送给Controller。如果涉及到针对某个个业务功能的调用,Controller会直接调用Model;如果呈现业务数据,Controller会通过Model获取相应业务数据并转换成View Model,最终通过View呈现出来。这样的交互协议方式反映了Controller针对Model的直接依赖。

如果我们在Controller激活系统中引入IoC,并采用IoC的方式提供用于处理请求的Controller对象,那么Controller和Model之间的依赖程度在很大程度上降低。我们甚至可以像下图所示的一样,以接口的方式都Model进行抽象,让Controller依赖于这个抽象化的Model接口,而不是具体的Model实现。

image

三、 创建基于IoC的自定义ControllerFactory

ASP.NET MVC的Controller激活系统最终通过ControllerFactory来创建目标Controller对象,要将IoC引入ASP.NET MVC并通过对应的IoC容器实现对目标Controller的激活,我们很自然地会想到自定义一个基于IoC的ControllerFactory。

对于IoC的ControllerFactory的创建,我们可以直接实现IControllerFactory接口创建一个全新的ControllerFactory类型,这需要实现包括Controller类型的解析、Controller实例的创建与释放以及会话状态行为选项的获取在内的所有功能。一般来说,Controller实例的创建与释放才收IoC容器的控制,为了避免重新实现其他的功能,我们可以直接继承DefaultControllerFactory,重写Controller实例创建于释放的逻辑。

实例演示:自定义一个基于Unity的ControllerFactory

现在我们通过一个简单的实例演示如何通过自定义ControllerFactory利用Unity进行Controller的激活与释放。为了避免针对Controller类型解析和会话状态行为选项的获取逻辑的重复定义,我们直接继承DefaultControllerFactory。我们将该自定义ControllerFactory命名为UnityControllerFactory,整个定义如下面的的代码片断所示。[源代码从这里下载]

   1: public class UnityControllerFactory : DefaultControllerFactory
   2: {
   3:     static object syncHelper = new object();
   4:   static Dictionary<string, IUnityContainer> containers = new Dictionary<string,IUnityContainer>();
   5:     public IUnityContainer UnityContainer { get; private set; }
   6:     public UnityControllerFactory(string containerName = "")
   7:     {
   8:         if (containers.ContainsKey(containerName))
   9:         {
  10:             this.UnityContainer = containers[containerName];
  11:             return;
  12:         }
  13:         lock (syncHelper)
  14:         {
  15:             if (containers.ContainsKey(containerName))
  16:             {
  17:                 this.UnityContainer = containers[containerName];
  18:                 return;
  19:             }
  20:             IUnityContainer container = new UnityContainer();
  21:             //配置UnityContainer
  22:             UnityConfigurationSection configSection = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) 
  23:                 as UnityConfigurationSection;           
  24:             if (null == configSection && !string.IsNullOrEmpty(containerName))
  25:             {
  26:                 throw new ConfigurationErrorsException("The <unity> configuration section does not exist.");
  27:             }
  28:             if (null != configSection )
  29:             {
  30:                 if(string.IsNullOrEmpty(containerName))
  31:                 {
  32:                     configSection.Configure(container);
  33:                 }
  34:                 else
  35:                 {
  36:                     configSection.Configure(container, containerName);
  37:                 }
  38:             }
  39:             containers.Add(containerName, container);
  40:             this.UnityContainer = containers[containerName];
  41:         }       
  42:     }
  43:   protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
  44:     {
  45:         if (null == controllerType)
  46:         {
  47:             return null;
  48:         }
  49:         return (IController)this.UnityContainer.Resolve(controllerType);
  50:     }
  51:     public override void ReleaseController(IController controller)
  52:     {
  53:         this.UnityContainer.Teardown(controller);
  54:     }
  55: }

UnityControllerFactory的UnityConainer属性表示的实现自Microsoft.Practices.Unity.IUnityContainer接口的对象表示定义在Unity中的IoC容器。为了避免UnityConainer对象的频繁创建,我们创建的UnityConainer对象保存在一个通过静态字段(containers)表示的字典对象中,其Key为UnityConainer的配置名称。构造函数中的参数containnerName表示使用的UnityConainer的配置名称,如果静态字典中存在着与之匹配的UnityConainer对象,则直接获取出来作为UnityConainer属性的值;否则创建一个新的UnityConainer对象并加载对应的配置对其进行相关设置,最后将其赋值给UnityConainer属性并添加到静态字典之中。

我们重写了定义在基类DefaultControllerFactory的虚方法GetControllerInstance,在解析出来的Controller类型(controllerType参数)不为Null的情况下,直接调用UnityConainer的Resolve方法激活对应的Controller实例。在用于释放Controller对象的ReleaseController方法中,我们直接将Controller对象作为参数调用UnityConainer的Teardown方法。

整个自定义的UnityControllerFactory就这么简单,为了演示IoC在它身上的体现,我们在一个简单的ASP.MVC实例中来使用我们刚刚定义的UnityControllerFactory。我们沿用在《ASP.NET的路由系统:URL与物理文件的分离》中使用过的关于“员工管理”的场景,如下图所示,本实例由两个页面(对应着两个View)组成,一个用于显示员工列表,另一个用于显示基于某个员工的详细信息。

image

我们通过Visual Studio的ASP.NET MVC项目模板创建一个空的Web应用,并添加针对Unity的两个程序集(Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll)引用。然后我们再Models目录下定义如下一个表示员工信息的Employee类型。

   1: public class Employee
   2: {
   3:     [Display(Name="ID")]
   4:     public string Id { get; private set; }
   5:     [Display(Name = "姓名")]
   6:     public string Name { get; private set; }
   7:     [Display(Name = "性别")]
   8:     public string Gender { get; private set; }
   9:     [Display(Name = "出生日期")]
  10:     [DataType(DataType.Date)]
  11:     public DateTime BirthDate { get; private set; }
  12:     [Display(Name = "部门")]
  13:     public string Department { get; private set; }
  14:  
  15:     public Employee(string id, string name, string gender, DateTime birthDate, string department)
  16:     {
  17:         this.Id = id;
  18:         this.Name = name;
  19:         this.Gender = gender;
  20:         this.BirthDate = birthDate;
  21:         this.Department = department;
  22:     }
  23: }

我们创建一个独立的组件来模拟用于维护应用状态提供业务操作功能的Model(在这里我们将ASP.NET MVC中的Model视为View Model),为了降低Controller和Model之间耦合度,我们为这个Model定义了接口。如下所示的IEmployeeRepository就代表了这个接口,唯一的方法GetEmployees用于获取所有员工列表(id参数值为空)或者基于指定ID的某个员工信息。

   1: public interface IEmployeeRepository
   2: {
   3:     IEnumerable<Employee> GetEmployees(string id = "");
   4: }

EmployeeRepository类型实现了IEmployeeRepository接口,具体的定义如下所示。简单起见,我们直接通过一个类型为List<Employee>得静态字段来表示所有员工信息的存储。

   1: public class EmployeeRepository: IEmployeeRepository
   2: {
   3:     private static IList<Employee> employees;
   4:     static EmployeeRepository()
   5:     {
   6:         employees = new List<Employee>();
   7:         employees.Add(new Employee(Guid.NewGuid().ToString(), "张三", "男", new DateTime(1981, 8, 24), "销售部"));
   8:         employees.Add(new Employee(Guid.NewGuid().ToString(), "李四", "女", new DateTime(1982, 7, 10), "人事部"));
   9:         employees.Add(new Employee(Guid.NewGuid().ToString(), "王五", "男", new DateTime(1981, 9, 21), "人事部"));
  10:     }
  11:     public IEnumerable<Employee> GetEmployees(string id = "")
  12:     {
  13:         return employees.Where(e => e.Id == id || string.IsNullOrEmpty(id));
  14:     }
  15: }

现在我们来创建我们的Controller,在这里我们将其起名为EmployeeController。如下面的代码片断所示,EmployeeController具有一个类型为IEmployeeRepository的属性Repository,应用在上面的DependencyAttribute特性我们知道这是一个“依赖属性”,如果采用UnityContainer来激活EmployeeController对象的时候,会根据注册的类型映射来实例化一个实现了IEmployeeRepository的类型的实例来初始化该属性。

   1: public class EmployeeController : Controller
   2: {
   3:     [Dependency]
   4:     public IEmployeeRepository Repository { get; set; }
   5:     public ActionResult Index()
   6:     {
   7:         var employees = this.Repository.GetEmployees();
   8:         return View(employees);
   9:     }
  10:     public ActionResult Detail(string id)
  11:     {
  12:         Employee employee = this.Repository.GetEmployees(id).FirstOrDefault();
  13:         if (null == employee)
  14:         {
  15:             throw new HttpException(404, string.Format("ID为{0}的员工不存在", id));
  16:         }
  17:         return View(employee);
  18:     }
  19: }

默认的Index操作方法中,我们通过Repository属性获取表示所有员工的列表,并将其作为Model显现在对应的View中。至于用于显示指定员工ID详细信息的Detail操作,我们同样通过Repository属性根据指定的ID获取表示相应员工信息的Employee对象,如果该对象为Null,直接返回一个状态为404的HttpException;否则作为将其作为Model显示在相应的View中。

如下所示的名为Index的View的定义,它的Model类型为IEnumerable<Employee>,在这里View中,我们通过一个表格来显示表示为Model的员工列表。值得一提的是,我们通过调用HtmlHelper的ActionLink方法将员工的名称显示为一个执行Detail操作的连接,作为路由变量参数集合中同时包含当前员工的ID和姓名。根据我们即将注册的路由规则,这个链接地址的格式为/Employee/Detail/{Name}/{Id}。

   1: @model IEnumerable<Employee>
   2: @{
   3:     ViewBag.Title = "Index";
   4: }
   5: <table id="employees" rules="all" border="1">
   6:     <tr>
   7:         <th>姓名</th>
   8:         <th>性别</th>
   9:         <th>出生日期</th>
  10:         <th>部门</th>
  11:     </tr>
  12:     @{
  13:         foreach(Employee employee in Model)
  14:         {
  15:             <tr>
  16:                 <td>@Html.ActionLink(employee.Name, "Detail", 
  17:                     new { name = employee.Name, id = employee.Id })</td>
  18:                 <td>@employee.Gender</td>
  19:                 <td>@employee.BirthDate.ToString("dd/MM/yyyy")</td>
  20:                 <td>@employee.Department</td>
  21:             </tr>
  22:         }
  23:     }    
  24: </table>

用于显示具有某个员工信息的名为Detail的View定义如下,这是一个Model类型为Employee的强类型的View,我们通过通过表格的形式将员工的详细信息显示出来。

   1: @model Employee
   2: @{
   3:     ViewBag.Title = Model.Name;
   4: }
   5: <table id="employee" rules="all" border="1">
   6:     <tr><td>@Html.LabelFor(m=>m.Id)</td><td>@Model.Id</td></tr>
   7:     <tr><td>@Html.LabelFor(m=>m.Name)</td><td>@Model.Name</td></tr>
   8:     <tr><td>@Html.LabelFor(m=>m.Gender)</td><td>@Model.Gender</td></tr>
   9:     <tr><td>@Html.LabelFor(m=>m.BirthDate)</td><td>@Model.BirthDate.ToString("dd/MM/yyyy")</td></tr>
  10:     <tr><td>@Html.LabelFor(m=>m.Department)</td><td>@Model.Department</td></tr>
  11: </table>

我需要在Global.asax中完成两件事情,即针对自定义UnityControllerFactory和路由的注册。如下的代码片断所示,在Application_Start方法中我们通过当前ControllerBuilder注册了一个UnityControllerFactory实例。 在RegisterRoutes方法中我们注册两个路由,前者针对Detail操作(URL模版包含员工的ID和姓名),后者针对Index操作。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     //其他成员
   4:     public static void RegisterRoutes(RouteCollection routes)
   5:     {
   6:         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
   7:         routes.MapRoute(
   8:             name: "Detail",
   9:             url: "{controller}/{action}/{name}/{id}",
  10:             defaults: new { controller = "Employee" }
  11:         );
  12:         routes.MapRoute(
  13:             name: "Default",
  14:             url: "{controller}/{action}",
  15:             defaults: new { controller = "Employee", action = "Index"}
  16:         );
  17:     }
  18:  
  19:     protected void Application_Start()
  20:     {
  21:         //其他操作
  22:         RegisterRoutes(RouteTable.Routes);
  23:         ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory());
  24:     }
  25: }

EmployeeController仅仅依赖于IEmployeeRepository接口,它通过基于该类型的依赖属性Repository返回员工信息,我们需要通过注册为之设置一个具体的匹配类型,而这个类型自然就是前面我们定义的EmployeeRepository。如下所示的正是Unity相关的类型注册配置。到此为止,整个实例的编程和配置工作既已完成(忽略了针对样式的设置),运行该程序就可以得到如上图所示的效果。

   1: <configuration>
   2:   <configSections>
   3:     <section name="unity" 
   4:          type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, 
   5:                Microsoft.Practices.Unity.Configuration"/>
   6:      </configSections>
   7:   <unity>
   8:     <containers>
   9:       <container>
  10:         <register type="Artech.Mvc.IEmployeeRepository, Artech.Mvc.MvcApp" 
  11:                   mapTo="Artech.Mvc.EmployeeRepository, Artech.Mvc.MvcApp"/>
  12:       </container>
  13:     </containers>
  14:   </unity>
  15: </configuration>

 

ASP.NET MVC Controller激活系统详解:总体设计
ASP.NET MVC Controller激活系统详解:默认实现
ASP.NET MVC Controller激活系统详解:IoC的应用[上篇]
ASP.NET MVC Controller激活系统详解:IoC的应用[下篇]


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
1028 10
|
C# Android开发 iOS开发
2025年全面的.NET跨平台应用框架推荐
2025年全面的.NET跨平台应用框架推荐
870 23
|
监控 前端开发 API
一款基于 .NET MVC 框架开发、功能全面的MES系统
一款基于 .NET MVC 框架开发、功能全面的MES系统
804 5
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
447 5
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
707 2
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
912 6
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
493 7
|
前端开发 JavaScript 开发工具
跨域联姻:React.NET——.NET应用与React的完美融合,解锁前后端高效协作新姿势。
【8月更文挑战第28天】探索React.NET,这是将热门前端框架React与强大的.NET后端无缝集成的创新方案。React以其组件化和虚拟DOM技术著称,能构建高性能、可维护的用户界面;.NET则擅长企业级应用开发。React.NET作为桥梁,使.NET应用轻松采用React构建前端,并优化开发流程与性能。通过直接托管React组件,.NET应用简化了部署流程,同时支持服务器端渲染(SSR),提升首屏加载速度与SEO优化。
803 1
|
存储 缓存 安全
.NET 在金融行业的应用:高并发交易系统的构建与优化之路
【8月更文挑战第28天】在金融行业,交易系统需具备高并发处理、低延迟及高稳定性和安全性。利用.NET构建此类系统时,可采用异步编程提升并发能力,优化数据库访问以降低延迟,使用缓存减少数据库访问频率,借助分布式事务确保数据一致性,并加强安全性措施。通过综合优化,满足金融行业的严苛要求。
651 1
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
403 0