Util应用程序框架公共操作类(一):数据类型转换公共操作类(介绍篇)

  本系列文章将介绍一些对初学者有帮助的辅助类,这些辅助类本身并没有什么稀奇之处,如何能发现需要封装它们可能更加重要,所谓授之以鱼不如授之以渔,掌握封装公共操作类的技巧才是关键,我会详细说明创建这些类的动机和思考过程,以帮助初学者发现和封装自己需要的东西。创建公共操作类的技巧,大家可以参考我的这篇文章——应用程序框架实战十二:公共操作类开发技巧(初学者必读)

  封装公共操作类,不仅要把技术上困难的封装进来,还需要不断观察自己的代码,以找出哪些部分可以更加简化。本文将介绍一个容易被大家所忽视的东西——数据类型转换。

  数据类型转换可以把某个源类型转换为目标类型,比如把字符串转换为整型。一种选择是,你可以使用System.Convert类进行转换。  

string input = "1";
int result = System.Convert.ToInt32( input);

  这样看起来好像没什么问题,不过input很明显不一定是个常量,如果你还在使用ASP.NET Web Form这样的技术,你需要在后置代码中从控件读取值。

string input = TextBox1.Text;
int result = System.Convert.ToInt32( input);

  当客户输入整数时,不会有什么问题,但他如果输入一个字母或汉字,上面代码会抛出一个异常,“System.FormatException: 输入字符串的格式不正确。”,这可能不是你想要的。当然,你可以在客户端进行JS验证,不过客户也可以绕过你的页面,直接POST到你的服务器,所以你在服务端必须处理这个问题。

  引发异常,并且你没有进行任何处理,可能导致一个黄页,让你的客户一惊。为了避免显示黄页,初学者大多直接在代码上加一个try-catch进行捕获,并给客户一个友情提示。

try {
  string input = TextBox1.Text;
  int result = System.Convert.ToInt32( input );
}
catch( Exception ex ) {
  //弹出消息框提示客户输入正确数据
}

  一旦吃到甜头,初学者发现这段代码可以实现他要的功能,不会过多考虑可维护性,于是会把这个结构向整个表现层复制,最终导致一个混乱的局面。

  除了表现层以外,数据访问层也经常需要进行数据类型转换。如果你还在使用原始的Ado.Net,从DataReader获取值。

IDataReader reader = cmd.ExecuteReader();
int result = System.Convert.ToInt32( reader["字段名"] );

  同样,为了不引发异常,初学者根据之前的经验,会在调用代码时添加try-catch进行异常捕获。

try {
  IDataReader reader = cmd.ExecuteReader();
  int result = System.Convert.ToInt32( reader["字段名"] );
}
catch( Exception ex ) {
  //有些人会在这里记录错误日志,还有些懒人直接留空,啥也不干
}        

  所以最终的结果是,只要进行数据类型转换的操作,初学者为了一定的系统健壮性会大量添加异常处理结构,从而导致代码混乱。

  另一个选择是,.Net提供了一个不引发异常的类型转换方法,比如,

string input = "1";
int result;
if( int.TryParse( input, out result ) == false ){
  //处理错误
}

  TryParse会返回一个bool值,指示转换是否成功,如果转换失败,你可以记录日志,并显示一个错误。不过大部分人都可能会偷懒,不会在这里进行任何处理。

string input = "1";
int result;
int.TryParse( input, out result );

  如果你不想引发异常,更不想用try-catch结构来捕获异常,这确实是一个更好的选择,但需要额外定义一个变量,作为out参数来获取值,会造成额外的工作量。

  当然,.Net技术也一直处于持续改进中,表现层技术进入到MVC和WPF时代,而数据访问技术也进入到了Entity Framework时代。MVC提供了一个叫做模型绑定的功能,用于将界面上传回的数据映射到控制器操作的参数中,并且这些参数可以支持实体,这是一个非常强大的功能,这样就不需要手工进行赋值了,更不需要类型转换。WPF通过双向数据绑定,Entity Framework通过映射器,都解决了类似问题。

  但是,并不是说数据类型转换就毫无用武之地了,考虑一个使用MVC的场景。你在界面上有一个表格,表格的每行都有一个checkbox,你可以打勾以选中某些行,然后你会把每行的实体标识传到控制器。如果实体标识的数据类型使用Guid,你可以用一个IList<Guid>来接收。

public ActionResult 方法名( IList<Guid> ids ) {
}

  这一般都行得通。但是,你并不总是可以这样干,出于某些特殊原因,有时候你需要自己在js中用逗号拼接一些Id,或者用一个Hidden把用逗号拼接的Id保存起来,然后作为一个字符串传到控制器。比如” 06d8dcdd-cfad-433c-aec0-87bf613b9457, 684c0c02-93ec-4047-8f16-57e36b50d703”。

public ActionResult 方法名( string ids ) {
}

  由于没有自动转换支持,你只好自己动手,这很简单,使用逗号把输入字符串打散成字符串数组,然后遍历每个数组元素,转换成Guid类型,再添加到一个结果集合保存起来就OK了。

public ActionResult 方法名( string ids ) {
  List<Guid> result = new List<Guid>();
  string[] list = ids.Split( ‘,‘ );
  foreach( var each in list ) {
    result.Add( Guid.Parse( each ) );
  }
  //在这里把result传到业务层
}

  你发现这段代码不仅可以处理带有逗号的字符串,像” 06d8dcdd-cfad-433c-aec0-87bf613b9457, 684c0c02-93ec-4047-8f16-57e36b50d703”,甚至还能处理不带逗号的,比如“06d8dcdd-cfad-433c-aec0-87bf613b9457 “。你非常满意,然后把这一段代码复制到所有需要将拼接字符串转换成集合的地方。

  没过多久,你在一个页面发现了Bug,以前测试的时候,都是操作多个Id,这次没进行操作,传过来的字符串是””,你的代码没有进行任何健壮性检测,所以失败了,抛出一个异常“无法识别的 GUID 格式。“。

  你安慰自己“这并不算什么技术问题,只是一时疏忽,看我加一个判断,一招将它搞定“。不过在你的下意识里,已经感觉到进行边界测试才是健壮性的关键。

public ActionResult 方法名( string ids ) {
  List<Guid> result = new List<Guid>();
  if ( !string.IsNullOrWhiteSpace( ids ) ) {
    string[] list = ids.Split( ‘,‘ );
    foreach( var each in list ) {
      result.Add( Guid.Parse( each ) );
    }
  }
  //在这里把result传到业务层
}

  你现在准备测试一下,传了一个””过来,果然有效,你大赞自己处理BUG的速度惊人,为了给其它地方也添加如此健壮的特性,你打开所有可能用到的页面,找到这几行代码,进行修改,虽然你感觉这确实有点无聊,但还是只有硬起头皮改了。

  从上面可以看到,虽然是司空见惯的数据类型转换,也还是有很多值得我们改进的地方。

  下一篇我将使用TDD方式把数据类型转换公共操作类开发出来。由于TDD并不是本系列介绍的重点,所以我不会在本文中详细介绍TDD的要点,请大家查看相关资料,如果有空,我会专门写一篇文章来分享我在使用TDD所碰到的障碍以及心得。

  .Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

  谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

时间: 2024-12-27 19:36:20

Util应用程序框架公共操作类(一):数据类型转换公共操作类(介绍篇)的相关文章

Util应用程序框架公共操作类(三):数据类型转换公共操作类(扩展篇)

上一篇以TDD方式介绍了数据类型转换公共操作类的开发,并提供了单元测试和实现代码,本文将演示通过扩展方法来增强公共操作类,以便调用时更加简化. 下面以字符串转换为List<Guid>为例进行讨论. string input = "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A"; var result = Util.Conv.ToGuidList( input ); 观察上面

应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)

前面已经介绍过Entity Framework的工作单元和映射层超类型的封装,从本文开始,将逐步介绍仓储以及对查询的扩展支持. 什么是仓储 仓储表示聚合的集合. 仓储所表现出来的集合外观,仅仅是一种模拟,除了测试以外,没有理由使用内存中真正的集合来创建仓储. 不应该为所有实体建立仓储,只有聚合才拥有仓储. 仓储用来重建已持久化的聚合,而工厂用于新建聚合. 使用仓储的优点 直接使用Entity Framework的DbContext不是很好吗,为什么还要在DbContext的上方封装一层仓储呢,这

Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)

今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误. 我增加了几个单元测试来捕获错误,代码如下. /// <summary> /// 测试值为枚举 /// </summary> [TestMethod] public void TestGetValue_Enum() { var test1 = new Test1(); test1.NullableEnumValue = LogType.Error; //属性为枚举,值

Util应用程序框架公共操作类(四):验证公共操作类

为了能够验证领域实体,需要一个验证公共操作类来提供支持.由于我将使用企业库(Enterprise Library)的验证组件来完成这项任务,所以本文也将演示对第三方框架的封装要点. .Net提供了一个称为DataAnnotations的验证技术,即在对象的属性上添加一些Attribute,比如[Required]用来验证必填项.这是非常强大的特性,通过附加元数据的方式来提供验证,甚至在Mvc框架中还能自动生成Js客户端验证,从而可以非常方便的实现客户端和服务端的双重验证. 但是遗憾的是,.Net

Util应用程序框架公共操作类(六):验证扩展

前面介绍了仓储的基本操作,下面准备开始扩展查询,在扩展查询之前,首先要增加两个公共操作类,一个是经常要用到的验证方法,另一个是Lambda表达式的操作类. 很多时候,我们会判断一个对象是否为null,由于null是一个不能接受的值,它会导致“未将对象引用设置到对象的实例”的严重错误,所以当检测到null值时一般直接抛出ArgumentNullException异常. public void Test( string name ) { if( name == null ) throw new Ar

Util应用程序框架公共操作类(五):异常公共操作类

任何系统都需要处理错误,本文介绍的异常公共操作类,用于对业务上的错误进行简单支持. 对于刚刚接触.Net的新手,碰到错误的时候,一般喜欢通过返回bool值的方式指示是否执行成功. public bool 方法名() { //执行代码,成功返回true,否则返回false } 不过上面的方法有一个问题是,无法知道确切的错误原因,所以需要添加一个out参数来返回错误消息. public bool 方法名( out string errorMessage ) { //执行代码,成功返回true,否则返

Util应用程序框架公共操作类(十一):表达式生成器

本篇介绍的表达式生成器,用于动态创建表达式. 在Util项目Lambdas目录中,添加ExpressionBuilder,代码如下. using System; using System.Linq.Expressions; namespace Util.Lambdas { /// <summary> /// 表达式生成器 /// </summary> public class ExpressionBuilder<TEntity> { /// <summary>

Util应用程序框架公共操作类(十):可空值类型扩展

当你使用可空的值类型时,你会发现取值很不方便,比如Guid? obj,你要从obj中获取值,可以使用Value属性obj. Value,但obj可能为null,这时候就会抛出一个异常. 可空值类型提供了一个HasValue属性,它可以识别出obj是不是一个null值,每当你获取可空值都需要加上这个判断if(value.HasValue){ var value = obj.Value;}. 下面我们通过几个扩展方法,把判断封装起来. 在Util项目中添加Extensions.Nullable.cs

应用程序框架实战三十:表现层及ASP.NET MVC介绍(一)

本文将介绍表现层及ASP.NET MVC的一些要点,特别是ASP.NET MVC的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET MVC4 高级编程>,作者Jon Galloway等,这本书由ASP.NET MVC团队成员编写,相当不错. 表现层的职责 表现层的职责是展示和收集数据,将领域层的数据和逻辑展示出来,并收集用户输入的相关信息. 搞清楚表现层的职责以后,你就应该清楚,表现层不是你应该编写业务逻辑的地方,这也是分层架构的核心. 如果要展示一个计算值,不应该在表现层