zoukankan      html  css  js  c++  java
  • MVC中两种实体:View Model和Business Model的简化(转载)

    在MVC的Model中,我们可以定义很多与视图相关的元数据,这些元数据对我们开发视图起着相当重要的作用,特别是在数据验证方面。这些元数据一般情况下我们是不会定义在业务实体(或持久化实体)上面,所以很多情况下,我们会需要开发两种实体:View Model和Business Model。这样就造成,在Action与View的沟通当中,我们需要使用View Model,然后在业务逻辑处理时,我们需要再将View Model映射到Business Model,这将会使我们的开发框架变得繁琐。因为一般情况下,View Model和Business Model在很多情况下,都是很雷同的对象,只是View Model会有很多与视图相关的元数据。在这种情况下,只要我们能把View Model作为Business Model的元数据描述对象(MetadataType)来使用,而不直接参与Action与View的沟通,让这些工作都由Business Model来承担,这样就可以有效的避免很多重复工作。

      在ASP.NET MVC内部,提供了MetadataTypeAttribute这个标签,让我们可以为Business Model指定它对应的视图元数据类型。特别是当我们使用LINQ2SQL、EF等框架来生成实体框架时,我们可以以partial类的形式来提供它对应的视图元数据类型:

    [MetadataType(typeof(Product_Metadata))]
    public partial class Product
    {}

    public class Product_Metadata
    {}

      这样做在大多数情况下是没有问题的。但是仅仅是这样,还不能解决所有问题。一般情况下Business Model和MetadataType是不在同一个Assembly里面,这时候你就无法以partial类的形式来扩展Business Model。所以我们就需要有一套机制来延迟注册Business Model与MetadataType的映射关系。通过MVC源码的分析,我们可以通过扩展 DataAnnotationsModelMetadataProvider的

      GetTypeDescriptor方法来解决这个问题。

      首先,我们先定义一个Business Model与MetadataType的映射容器:

    public static class TypeDescriptorHelper
    {
    static Hashtable hashtable = new Hashtable();
    static ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
    static TypeDescriptorHelper()
    {
    }
    public static void RegisterMetadataType(Type type, Type metadataType)
    {
    locker.EnterWriteLock();
    hashtable[type] = metadataType;
    locker.ExitWriteLock();
    }
    public static ICustomTypeDescriptor Get(Type type)
    {
    locker.EnterReadLock();
    var metadataType = hashtable[type] as Type;
    ICustomTypeDescriptor descriptor = null;
    if (metadataType != null)
    {
    descriptor = new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType).GetTypeDescriptor(type);
    }
    locker.ExitReadLock();
    return descriptor;
    }
    }

      这个容器非常简单,我们只是定义了一个hashtable来保存它们的映射关系。然后,我们从 DataAnnotationsModelMetadataProvider继承一个新的类,重写GetTypeDescriptor方法:

    public class CustomDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
    {
    protected override System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor(Type type)
    {
    var descriptor = TypeDescriptorHelper.Get(type);
    if (descriptor == null)
    {
    descriptor = base.GetTypeDescriptor(type);
    }
    return descriptor;
    }
    }

      接下来要做的事情就是把CustomDataAnnotationsModelMetadataProvider注册到系统中,用它来代替原来的 DataAnnotationsModelMetadataProvider,在Global.asax添加如下代码:

    ModelMetadataProviders.Current = new CustomDataAnnotationsModelMetadataProvider();

      最后需要在映射容器中,添加你希望的映射关系:

    TypeDescriptorHelper.RegisterMetadataType(typeof(Product), typeof(Product_Metadata));

      通过这样的映射,我们就可以完全解放Business Model对MetadataType的依赖了。

      后记: 在调试这个扩展的过程当中,出现了一个相当低级的错误,花了我几个小时的时间。话说提交的数据中有一个名为site的字符串(在Route.Values 里面),同时我在Action的参数中,定义的是一个名为site的复杂类型。程序在运行过程中,始终提示异常:类型无法转换。因为我怀疑是我扩展了 MetadataType引发的问题,所以拼命的在那边调试,始终没有找到原因。最后还原所有的代码,问题依旧,才让我走到正确的方向上,冤枉死我了。

  • 相关阅读:
    Combine 框架,从0到1 —— 4.在 Combine 中使用计时器
    Combine 框架,从0到1 —— 4.在 Combine 中使用通知
    Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度
    Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布
    使用 Swift Package Manager 集成依赖库
    iOS 高效灵活地配置可复用视图组件的主题
    构建个人博客网站(基于Python Flask)
    Swift dynamic关键字
    Swift @objcMembers
    仅用递归函数操作逆序一个栈(Swift 4)
  • 原文地址:https://www.cnblogs.com/marslin/p/3728019.html
Copyright © 2011-2022 走看看