MVC中两种实体:View Model和Business Model的简化(转载)

摘要:
通常,我们不会在业务实体(或持久实体)上定义这些元数据。特别是当我们使用LINQ2SQL、EF和其他框架来生成实体框架时,我们可以以分部类的形式提供它们对应的视图元数据类型:

在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引发的问题,所以拼命的在那边调试,始终没有找到原因。最后还原所有的代码,问题依旧,才让我走到正确的方向上,冤枉死我了。

免责声明:文章转载自《MVC中两种实体:View Model和Business Model的简化(转载)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇pycharm切换conda虚拟环境点击div掉下和上升下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

极简配置phpstorm+xdebug进行断点调试

以前调试的时候各种var_dump()就能得到结果,现在入手别人开发的工作,由于不了解业务和代码逻辑,又要去修改bug,就造成了修改bug效率低,所以又拾起来了xdbug,顺便总结了一下phpstor配合xdebug的基本配置   window7+phpstudy >>> php5.6 + nginx   1.停止phpstudy,不要直...

运行软件出现:模块“msvcp110.dll”已加载,但找不到入口点DllRegister

根据百度大多数回答来说 1:先是出现   无法启动程序,因为计算机丢失mfc110.dll 尝试重新安装该程序以解决问题   错误处理:下载或者在别人电脑上拷一份 如:'msvcp110.dll‘ 这类文件然后复制粘贴到系统盘即使/C/windows/system32或者64位系统就放到syswow64     楼主亲测是不能成功的。至少大多数情况是这样...

HashMap负载因子为什么是0.75

待写 HashMap负载因子为什么是0.75?HashMap有一个初始容量大小,默认是16static final int DEAFULT_INITIAL_CAPACITY = 1 << 4; // aka 16    为了减少冲突概率,当HashMap的数组长度达到一个临界值就会触发扩容,把所有元素rehash再放回容器中,这是一个非常耗时的...

java模板、工厂设计模式在项目中的重构

场景描述:   一个controller中,部门领导有布置任务,查看任务整体情况,查看部门成员,查看部门成员完成情况,导出任务详情,如下:    @RestController @RequestMapping(value = "/task") public class TaskController{...

Hive-json表-处理JSON格式数据

add jar /home/jasonapp/json-serde-1.3.7-jar-with-dependencies.jar; create external table if not exists dm_goddog.student( student map<string,string> comment "学生信息", class m...

MySQL索引优化

一、单表 创建索引之前:type=ALL全表扫描,Extra里面的Using filesort(文件内部排序) 根据where后面的条件创建:CREATE INDEX idx_article_ccv ON article(category_id,comments,views);    可以看出type由ALL变成了range,但是Extra里面的Usi...