随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了。

其实也许我们每天面对的太多东西了,觉得很多都稀松平常了,即使很细微的地方,可能我们都已经形成习惯了。反过来,如果我们切换到其他领域,如IOS、android,那么开始我们可能对里面很多设计的规则不甚了解,开始可能也是一头雾水。

本篇继续上一篇《循序渐进开发WinForm项目(1) --数据库设计和项目框架的生成》,继续介绍如何循序渐进开发Winform项目,继续分析介绍Winform的项目代码,从而让我们更加了解其中的分层和项目框架的组成等内容。

1、数据访问接口的定义

上面我们分析了实体类的定义,本节继续分析其他部分的内容,如数据访问接口成的定义如下所示。

namespace WHC.TestProject.IDAL
{
/// <summary>
/// 客户信息
/// </summary>
public interface ICustomer : IBaseDAL<CustomerInfo>
{
}
}

这里面的代码很简单,没有多余的代码行,那么里面究竟发生了什么呢,其中的IBaseDAL又是什么定义呢?

其实,IBaseDAL就是定义了很多我们开发用到的基础接口,如标准的增删改查,以及衍生出来的一些其他接口,如分页查询,条件查询等接口内容。这个ICustomer就是用来定义一些除了标准接口不能实现外的业务接口。

IBaseDAL通过传入一个实体类,从而方便给基类接口提供强类型的数据类型指定,提高我们的开发效率,减少出错的机会。

我们可以在VS里面查看IBaseDAL的定义,如下所示:

可以看到里面很多相关的接口定义,有返回实体T的,也有返回List<T>的,还有DataTable类型等等,这些基础接口,经过我们多个项目的应用实践,已逐步稳定并能够提供很好的接口支持,方便我们快速调用处理。

即使我们在没有实现任何业务接口的情况下,仅仅利用标准的基类API,也基本上能够完成绝大多数的数据操作功能了。

2、数据访问接口实现类的定义

我们分析完IDAL的数据访问接口成的定义后,继续了解一下,如何基于这个接口进行访问层的实现设计的。数据访问的实现层在项目中的位置如下所示(以基于SqlServer的DALSQL层进行分析)。

它的类代码定义如下所示。

namespace WHC.TestProject.DALSQL
{
/// <summary>
/// 客户信息
/// </summary>
public class Customer : BaseDALSQL<CustomerInfo>, ICustomer
{

数据访问接口实现层和接口定义层一样,都有一个基类,如基于SqlServer实现的基类为BaseDALSQL,这个基于SqlServer的数据访问基类,它也是继承自一个超级基类(大多数的实现在这里)AbstractBaseDAL。他们之间的继承关系如下所示

而我们刚才在项目工程的图里面看到,BaseDALSQL、IBaseDAL、AbstractBaseDAL这些类库由于具有很大的通用性,为了减少在不同的项目中进行复制导致维护问题,因此我们全部把这些经常使用到的基类或者接口,抽取到一个独立的类库里面,为了和普通的DotNET公用类库命名进行区分(WHC.Framework.Commons),我们把它命名为WHC.Framework.ControlUtil。

BaseDALSQL基类的定义如下所示。

这样做的好处是,在所有的模块里面,避免复制导致的版本维护问题,同时也减少代码的重复生成,增量生成的全部代码,可以一次性复制到整个项目工程里面,而不会导致基础类库的替换,因为这些基类不在生成目录里面,所有生成的类文件,都是和业务表相关的,如下所示。

具体的数据访问实现类(如Customer),它把数据库信息转换为实体类,有一个函数,在代码生成的时候已经生成;同时在把实体类的属性保存到数据库也有一个类似CRM的映射关系,从而实现可空的字段获取和更新操作。

        /// <summary>
/// 将DataReader的属性值转化为实体类的属性值,返回实体类
/// </summary>
/// <param name="dr">有效的DataReader对象</param>
/// <returns>实体类对象</returns>
protected override CustomerInfo DataReaderToEntity(IDataReader dataReader)
{
CustomerInfo info = new CustomerInfo();
SmartDataReader reader = new SmartDataReader(dataReader); info.ID = reader.GetString("ID");
info.Name = reader.GetString("Name");
info.Age = reader.GetInt32("Age");
info.Creator = reader.GetString("Creator");
info.CreateTime = reader.GetDateTime("CreateTime"); return info;
} /// <summary>
/// 将实体对象的属性值转化为Hashtable对应的键值
/// </summary>
/// <param name="obj">有效的实体对象</param>
/// <returns>包含键值映射的Hashtable</returns>
protected override Hashtable GetHashByEntity(CustomerInfo obj)
{
CustomerInfo info = obj as CustomerInfo;
Hashtable hash = new Hashtable(); hash.Add("ID", info.ID);
hash.Add("Name", info.Name);
hash.Add("Age", info.Age);
hash.Add("Creator", info.Creator);
hash.Add("CreateTime", info.CreateTime); return hash;
}

3、业务逻辑层的实现分析

分析完成了数据访问层的接口和实现类后,我们来进一步看看业务逻辑层的实现分析,由于数据访问层的本意是基于特定的数据库实现,因此业务逻辑层就是抽象不同的数据库,让它们根据配置,指向不同的数据库实现类,从而实现多数据库的支持。

namespace WHC.TestProject.BLL
{
/// <summary>
/// 客户信息
/// </summary>
public class Customer : BaseBLL<CustomerInfo>
{
public Customer() : base()
{
base.Init(this.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
}
}
}

业务逻辑层的代码也很简单,在构造函数里面Init一下即可,之所以使用这个Init操作,其实为了确定BLL层的业务对象名称和指定在哪个程序集里面进行构造的需要,让给基类进行必要的创建工作。

在BaseBLL的Init函数里面,我们根据子类传入的相关参数,由于我们约定了数据访问类的命名空间,因此只根据数据库配置的不同需要,替换部分名称,就可以具体的构造出一个数据访问类了。

            #region 根据不同的数据库类型,构造相应的DAL层
AppConfig config = new AppConfig();
string dbType = config.AppConfigGet("ComponentDbType");
if (string.IsNullOrEmpty(dbType))
{
dbType = "sqlserver";
}
dbType = dbType.ToLower(); string DALPrefix = "";
if (dbType == "sqlserver")
{
DALPrefix = "DALSQL.";
}
else if (dbType == "access")
{
DALPrefix = "DALAccess.";
}
else if (dbType == "oracle")
{
DALPrefix = "DALOracle.";
}
else if (dbType == "sqlite")
{
DALPrefix = "DALSQLite.";
}
else if (dbType == "mysql")
{
DALPrefix = "DALMySql.";
}
#endregion this.dalName = bllFullName.Replace(bllPrefix, DALPrefix);//替换中级的BLL.为DAL.,就是DAL类的全名
baseDal = Reflect<IBaseDAL<T>>.Create(this.dalName, dalAssemblyName);//构造对应的DAL数据访问层的对象类

这样精确构造出来的数据库访问访问对象,并把它转换为基类接口,那么就可以在BaseBLL类里的基类接口进行调用了。

而构造业务对象,通过BLLFactory<T>的泛型工厂,更能够精确构造出对应的业务对象类,这样构造出来的对象具有强类型,非常方便使用。

以上就是业务逻辑层,数据访问层和数据访问接口层的设计关系,为了高效进行开发工作,我们一定要使用强类型的接口调用,这样可以大大减少出错机会,而返回的基类接口,由于传入了特定的具体类型T,也能够构造出强类型的列表或者对象。因此,合理利用泛型,能够是我们的开发体验更加美好,更加高效。

循序渐进开发WinForm项目(2)--项目代码的分析的更多相关文章

  1. 循序渐进开发WinForm项目(6)--开发使用混合式Winform模块

    1.Winform数据访问模式定义 传统的Winform程序模块:用于传统的数据库通讯获取数据,这种方式获取数据,方便快捷,可以用于常规的业务系统的场景,用于单机版软件或者基于局域网内的业务系统软件. ...

  2. 循序渐进开发WinForm项目(5)--Excel数据的导入导出操作

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  3. 循序渐进开发WinForm项目(4)--Winform界面模块的集成使用

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  4. 循序渐进开发WinForm项目(3)--Winform界面层的项目设计

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  5. 循序渐进开发WinForm项目(1) --数据库设计和项目框架的生成

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  6. 如何快速开发Winform应用系统

    在实际的业务中,往往还有很多需要使用Winform来开发应用系统的,如一些HIS.MIS.MES等系统,由于Winform开发出来的系统界面友好,响应快速,开发效率高等各方面原因,还有一些原因是独立的 ...

  7. Vue项目开发最新、最全代码规范文档

    Vue项目开发最新.最全代码规范文档 2019年02月21日 10:43:49 yw00yw 阅读数 337   一. 目录结构 |— build 构建脚本目录 |— build.js 生产环境构建( ...

  8. DevExpress控件开发常用要点(项目总结版)

    使用DevExpress控件来做项目开发已经有很长一段时间了,在摸索开发到客户苛刻要求的过程中,其中碰到过很多问题需要解决的,随着一个个问题的解决,也留下很多对DevExpress控件的使用经验及教训 ...

  9. MVC5 网站开发之二 创建项目

    昨天对项目的思路大致理了一下,今天先把解决方案建立起来.整个解决包含Ninesky.Web.Ninesky.Core,Ninesky.DataLibrary等3个项目.Ninesky.Web是web应 ...

随机推荐

  1. js数据类型判断和数组判断

    这么基础的东西实在不应该再记录了,不过嘛,温故知新~就先从数据类型开始吧 js六大数据类型:number.string.object.Boolean.null.undefined string: 由单 ...

  2. 记录javascript 验证字符串布尔类型 及url 参数获取

    /^true$/i.test("false");false/^true$/i.test("true");true //获取请求参数的值 function Req ...

  3. Digg Reader 登录不了,原来如此

    cdnjs.cloudflare.com 不能访问,你懂的,给 https://cdnjs.cloudflare.com 跟 http://cdnjs.cloudflare.com 架个梯子就可以了, ...

  4. IT Operations(IT 运营),运维的更价值化认识

    一直想努力向别人(甚至包括从事运维的人)解释清楚什么是运维,发现很难! 6月20号,在InfoQ高效运维群里面,对运维创业做了一次激烈的讨论,很自然地,过程中不可避免的谈到运维苦逼和运维无法产品化的问 ...

  5. Etl之HiveSql调优(union all)

    相信在Etl的过程中不可避免的实用union all来拼装数据,那么这就涉及到是否并行处理的问题了. 在hive中是否适用并行map,可以通过参数来设定: set hive.exec.parallel ...

  6. CORS Cookie,和nodejs中的具体实现

    CORS(跨来源资源共享协议),高级浏览器(Chrome,firefox, opera, safir, ie10)在 XMLHttpRequest(AJAX) 中已经支持了这个协议.可以实现ajax跨 ...

  7. CentOS安装epel

    Centos5安装    rpm -ivh http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm   rpm  ...

  8. VPN各种常见状态码及修复方法

    1.633错误 :由于Windows系统本身的问题,在PPTP协议连接多次并断开之后,后导致一直出现633错误.参见微软的官方解决方案:http://support.microsoft.com/kb/ ...

  9. CGContext 的一些工具方法

    /* *设置虚线 *param context CGContext *param context CGContext *param lineDashLengths 如: const CGFloat l ...

  10. [LeetCode] “全排列”问题系列(二) - 基于全排列本身的问题,例题: Next Permutation , Permutation Sequence

    一.开篇 既上一篇<交换法生成全排列及其应用> 后,这里讲的是基于全排列 (Permutation)本身的一些问题,包括:求下一个全排列(Next Permutation):求指定位置的全 ...