.NET/ASP.NETMVC 大型站点架构设计—迁移Model元数据设置项(自定义元数据提供程序)

简介:

阅读目录:

  • 1.需求背景介绍(Model元数据设置项应该与View绑定而非ViewModel)

    • 1.1.确定问题域范围(可以使用DSL管理问题域前提是锁定领域模型)


  • 2.迁移ViewModel设置到外部配置文件(扩展Model元数据提供程序)

    • 2.1.实现元数据提供程序(简单示例)


1.需求背景介绍(Model元数据设置项应该与View绑定而非ViewModel)

使用ASP.NETMVC构建普通的中小型站点可以使用简单的Model元数据设置方式来控制ViewModel如何显示在View中,但是复杂的应用场景不会这么简单的就能完成;大型站点的ViewModel的体积非常大,真的大的超乎我们的想象(当然这里面有历史原因),这么大的一个显示实体我们需要在不同的页面中呈现它会非常的棘手;然而小型站点不太会遇见ViewModel在几十个页面中显示的情况出现,一般页面也就是几十个差不多了;

在大型电子商务应用中,UI层的一个ViewModel不仅用来呈现数据还充当着与远程SOA接口通讯的DTO作用,如果为了结构清晰完全可以将ViewModel与DTO分开,但是有时候我们确实需要考虑额外的性能开销(有时候我们只能接受历史遗留的问题,技术债务累积多久就要还多久);

这篇文章将讲解如何利用ASP.NETMVC开发大型站点时ViewModel中设置的元数据设置项随着不同的业务View不同而调用不同的元数据设置项,简单的讲也就是我们不会直接在ViewModel上应用元数据控制特性,而是通过将Model元数据设置项与具体的View绑定的方式来控制它在不同的View中运用不同的元数据控制项,元数据控制特性不会和具体的ViewModel绑定而是和具体的View绑定,因为只有View才是固定呈现什么内容,而ViewModel是用来共用的显示数据项的容器,我将通过本篇文章来讲解如何设计这样的高扩展性的ASP.NETMVC ViewModel使用结构;

1.2.确定问题域范围(可以使用DSL管理问题域前提是锁定领域模型)

在考虑使用配置文件将所需要的东西配置起来的时候,我们需要先确定到底需要将什么配置起来;这就需要我们先确定问题域,其实这也就是面向DSL编程的方法;

DSL:简单理解为面向特定领域的语言;该语言主要用来解决特定领域的实现问题,刚开始我们可以会把这个概念定义的过于庞大,希望能通过DSL解决一切领域问题,其实这是错误的;DSL其实是一小部分领域问题的提炼,如:我们这里的将ModelMetadata设置特性从原来定义在ViewModel上的迁移到外部去,这其中的主要问题域就是将ModelMetadata设置项与View绑定,而不是ViewModel;

只有先准确的找到问题域之后我们才能设计DSL来充分的表达这个问题域,通过XML能很好的表达任何特定领域结构的模型,当然你完全可以自己去设计DSL;

wKioL1Lcu8iwXQu9AAKOmvUhr9o430.jpg

目前对ViewModel中设置的元数据控制特性都会作用于使用该ViewModel的所有View,我们要解决的问题是将上图中的ModelMetadata域提取出去与View进行绑定,从而得到一个干净的ViewModel和灵活的面向View的元数据控制功能;当我们成功迁移之后,我们将得到下图中的结构;

wKiom1LcvATA23U_AALqvBadUhY734.jpg

最终我们会得出这样的一个满足实际需求的结构;

2.迁移ViewModel设置到外部配置文件(扩展Model元数据提供程序)

要想成功迁移设置项我们必须要搞清楚ASP.NETMVC中Model元数据提供程序的原理,这样我们才能将原来获取元数据的方式改变成我们自己的获取策略;在元数据提供程序对象模型中主要的功能分为两部分(这里我们只介绍获取元数据过程):

wKiom1LcvCXhnVA6AAE0vo5OTB8326.jpg

我们需要将BuildModelMetadata功能区域换成我们自己的策略;

wKioL1LcvBWiNp8wAAI79GDi5Hw775.jpg

这样我们就可以将一组强大的元数据提供程序植入到ASP.NETMVC框架的内部;

通过CustomModelMetadataProviderFactory创建用于获取任何一个外部类型的元数据提供程序对象,比如:CustomModelMetadataProviderWithDb(用于数据库的接口),CustomModelMetadataProviderWithConfig(用户配置文件),CustomModelMetadataProviderWithService(远程Service);

迁移ModelMetadate缓存数据(紧要关头可以进行内存优化)

在ASP.NETMVC内部提供了用来获取某个ViewModel的ModelMetadata的提供程序,通过该入口我们将可以把Model元数据缓存在我们自己的容器中,当然绝佳的缓存位置就是当前应用服务器的本地进程,这里是最好的缓存位置,我们缓存元数据主要不是为了改变它的存放位置而是要改变它获取的途径和方式,这样其实会有很多好处,比如:通过工具化管理内存中的缓存数据,对其进行压缩等等,因为你已经可以控制其获取元数据的过程,这在紧要关头可能就是救命稻草,这里只是一点扩展性的介绍,当然要看具体的需求了,不过这确实是一个好的思路;

2.1.实现元数据提供程序(简单示例)

View、ViewModel、ModelMetadata 映射设计:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using  System.Collections.Generic;
using  System.Linq;
using  System.Web.Mvc;
namespace  MvcApplication4.Seed
{
     public  enum  View
     {
         HomePage_Index,
         HomePage_Edit
     }
     public  enum  ViewModel
     {
         Customer
     }
     public  class  ViewMappingModelMetadata
     {
         public  View View {  get set ; }
         public  ViewModel ViewModel {  get set ; }
         public  ModelMetadata Metadata {  get set ; }
     }
     public  class  ViewMappingModelMetadataCollection : Dictionary<View, List<ViewMappingModelMetadata>>
     {
         private  static  ViewMappingModelMetadataCollection coll =  new  ViewMappingModelMetadataCollection();
         static  ViewMappingModelMetadataCollection()
         {
             //在Homepage下的视图———来自外部文件的接口,这里只是示例显示
             coll.Add(View.HomePage_Index,  new  List<ViewMappingModelMetadata>());
             coll[View.HomePage_Index].Add( new  ViewMappingModelMetadata()
             {
                 View = View.HomePage_Index,
                 ViewModel = ViewModel.Customer,
                 Metadata =
                     new  ModelMetadata(CustomModelMetadataProviderWithConfig.CurrentProvider,  typeof (Models.Customer),
                     () => {  return  new  Models.Customer().CustomerId; },  typeof ( string ),  "CustomerId" )
                     {
                         DisplayFormatString =  @"HomePage\DisplayName:{0}"
                     }
             });
             //在EditCustomer下的视图——来自外部文件的接口,这里只是示例显示
             coll.Add(View.HomePage_Edit,  new  List<ViewMappingModelMetadata>());
             coll[View.HomePage_Edit].Add( new  ViewMappingModelMetadata()
             {
                 View = View.HomePage_Edit,
                 ViewModel = ViewModel.Customer,
                 Metadata =  new  ModelMetadata(
                     CustomModelMetadataProviderWithConfig.CurrentProvider,  typeof (Models.Customer),
                     () => {  return  new  Models.Customer().CustomerId; },  typeof ( string ),  "CustomerId" )
                     {
                         DisplayFormatString =  @"Edit\DisplayName:{0}"
                     }
             });
         }
         public  static  ViewMappingModelMetadataCollection Current
         {
             get  return  coll; }
         }
         public  ModelMetadata GetMetadataByView(View view, ViewModel model)
         {
             var  metaList =  from  item  in  coll[view]  where  item.ViewModel == model  select  item.Metadata;
             return  metaList !=  null  && metaList.Count() > 0 ? metaList.LastOrDefault() :  null ;
         }
     }
}

wKiom1LcvGOyaIhUAAMWffXtoP8729.jpg

这两段是要被放到框架内部去完成的,这里只是为了演示其元数据的设置原理,所以简单这么写;

System.Web.Mvc.ModelMetadataProvider 实现自定义元数据提供程序:

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
using  System;
using  System.Collections.Generic;
using  System.Web.Mvc;
namespace  MvcApplication4.Seed
{
     public  class  CustomModelMetadataProviderWithConfig : System.Web.Mvc.ModelMetadataProvider
     {
         private  static  CustomModelMetadataProviderWithConfig provider =  new  CustomModelMetadataProviderWithConfig();
         public  static  CustomModelMetadataProviderWithConfig CurrentProvider
         {
             get  return  provider; }
         }
         public  override  IEnumerable<ModelMetadata> GetMetadataForProperties( object  container, Type containerType)
         {
             throw  new  NotImplementedException(); //复杂类型实现,属性的循环获取
         }
         public  override  ModelMetadata GetMetadataForProperty(Func< object > modelAccessor, Type containerType,  string  propertyName)
         {
             throw  new  NotImplementedException(); //复杂类型实现,属性的循环获取
         }
         public  override  ModelMetadata GetMetadataForType(Func< object > modelAccessor, Type modelType)
         {
             if  (modelAccessor ==  null return  null ;
             if  (System.Web.HttpContext.Current.Session[ "viewname" ] ==  null return  null ;
             var  result = ViewMappingModelMetadataCollection.Current.GetMetadataByView(
                     (View)System.Web.HttpContext.Current.Session[ "viewname" ], (ViewModel)System.Web.HttpContext.Current.Session[ "viewmodel" ]);
             if  (modelAccessor !=  null )
                 result.Model = modelAccessor().GetType().GetProperty( "CustomerId" ).GetValue(modelAccessor());
             return  result;
         }
     }
}

Customer模型定义:

1
2
3
4
public  class  Customer
{
     public  string  CustomerId {  get set ; }
}

在模型上我们没有应用任何一个 元数据控制特性,但是我们将在界面上看到效果;

View 视图定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@model  MvcApplication4.Models.Customer
< table >
     < tr >
         < td >
             < h2 >编辑模式.</ h2 >
             < h3 >@Html.DisplayForModel(Model.CustomerId)</ h3 >
         </ td >
     </ tr >
</ table >
@model  MvcApplication4.Models.Customer
< table >
     < tr >
         < td >
             < h2 >显示模式.</ h2 >
             < h3 >@Html.EditorForModel(Model.CustomerId)</ h3 >
         </ td >
     </ tr >
</ table >

这是两种模型的呈现方式;

wKioL1LcvIDwwb2EAACcV13jnlc872.jpg

wKiom1LcvLODkgW3AACQDh8jEJI862.jpg

我们自动设置的元数据已经起到效果了;





 本文转自 王清培 51CTO博客,原文链接:http://blog.51cto.com/wangqingpei557/1353112,如需转载请自行联系原作者


相关文章
|
23天前
|
数据管理 Nacos 开发者
"Nacos架构深度解析:一篇文章带你掌握业务层四大核心功能,服务注册、配置管理、元数据与健康检查一网打尽!"
【10月更文挑战第23天】Nacos 是一个用于服务注册发现和配置管理的平台,支持动态服务发现、配置管理、元数据管理和健康检查。其业务层包括服务注册与发现、配置管理、元数据管理和健康检查四大核心功能。通过示例代码展示了如何在业务层中使用Nacos,帮助开发者构建高可用、动态扩展的微服务生态系统。
68 0
|
1月前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
2月前
|
开发框架 前端开发 .NET
VB.NET中如何利用ASP.NET进行Web开发
在VB.NET中利用ASP.NET进行Web开发是一个常见的做法,特别是在需要构建动态、交互式Web应用程序时。ASP.NET是一个由微软开发的开源Web应用程序框架,它允许开发者使用多种编程语言(包括VB.NET)来创建Web应用程序。
59 5
|
3月前
|
开发框架 .NET Docker
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
|
3月前
|
数据可视化 NoSQL Serverless
现代化 Web 应用构建问题之Serverless架构的Web站点费用计算如何解决
现代化 Web 应用构建问题之Serverless架构的Web站点费用计算如何解决
43 1
|
3月前
|
开发框架 JSON .NET
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
|
3月前
|
开发框架 .NET 开发工具
【Azure 应用服务】App Service 的.NET Version选择为.NET6,是否可以同时支持运行ASP.NET V4.8的应用呢?
【Azure 应用服务】App Service 的.NET Version选择为.NET6,是否可以同时支持运行ASP.NET V4.8的应用呢?
|
4月前
|
开发框架 搜索推荐 前端开发
【.NET全栈】ASP.NET开发Web应用——Web部件技术
【.NET全栈】ASP.NET开发Web应用——Web部件技术
|
3月前
|
开发框架 .NET 数据库连接
ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?
ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?
|
3月前
|
开发框架 JavaScript .NET
Vue与ASP.NET Core Web Api设置localhost与本地ip地址皆可访问
Vue与ASP.NET Core Web Api设置localhost与本地ip地址皆可访问
41 0
下一篇
无影云桌面