当前位置: 首页 > news >正文

.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 映射设计:

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 实现自定义元数据提供程序:

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模型定义:

public class Customer
{
    public string CustomerId { get; set; }
}

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

View 视图定义:

@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

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

作者:王清培

出处:http://wangqingpei557.blog.51cto.com/

本文版权归作者和51CTO共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


相关文章:

  • VirtualBox和VMware安装Mac OS 10.11——虚拟机安装黑苹果
  • DOS建立带点的文件夹
  • 算法之智能搜索(上)
  • 一种基于云存储架构的开放电子病历平台
  • ES6核心特性
  • greenDAO讲义(一):使用篇
  • vue中完美解决html2canvas图片跨域问题
  • Eclipse+kafka集群 实例源码
  • 高并发处理思路与手段(六):服务降级与服务熔断
  • 2014第6周日杂记
  • 26. Remove Duplicates from Sorted Array C++ 删除排序数组中的重复项
  • Usage of API documented as @since 1.8+”报错的解决办法
  • Mysql容器启动失败-解决方案
  • Linux的epoll模型
  • java B2B2C电子商务平台分析之七-Spring Cloud Config
  • C++类的相互关联
  • C++入门教程(10):for 语句
  • crontab执行失败的多种原因
  • IDEA 插件开发入门教程
  • in typeof instanceof ===这些运算符有什么作用
  • Javascripit类型转换比较那点事儿,双等号(==)
  • js ES6 求数组的交集,并集,还有差集
  • Kibana配置logstash,报表一体化
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • - 概述 - 《设计模式(极简c++版)》
  • 将 Measurements 和 Units 应用到物理学
  • 悄悄地说一个bug
  • 软件开发学习的5大技巧,你知道吗?
  • 手写一个CommonJS打包工具(一)
  • 王永庆:技术创新改变教育未来
  • 微信小程序实战练习(仿五洲到家微信版)
  • 原生 js 实现移动端 Touch 滑动反弹
  • 最简单的无缝轮播
  • 【云吞铺子】性能抖动剖析(二)
  • PostgreSQL之连接数修改
  • 进程与线程(三)——进程/线程间通信
  • 整理一些计算机基础知识!
  • #pragma multi_compile #pragma shader_feature
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (12)目标检测_SSD基于pytorch搭建代码
  • (2015)JS ES6 必知的十个 特性
  • (floyd+补集) poj 3275
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (办公)springboot配置aop处理请求.
  • (分布式缓存)Redis分片集群
  • (附源码)springboot教学评价 毕业设计 641310
  • (九)One-Wire总线-DS18B20
  • (四)图像的%2线性拉伸
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .NET/C# 使用反射注册事件
  • .NET中使用Redis (二)
  • @Autowired和@Resource的区别