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

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

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

1、数据访问接口的定义

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

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

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

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

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

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

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

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

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

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

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

namespaceWHC.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 overrideCustomerInfo DataReaderToEntity(IDataReader dataReader)
{
CustomerInfo info
= newCustomerInfo();
SmartDataReader reader
= newSmartDataReader(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");returninfo;
}
/// <summary> ///将实体对象的属性值转化为Hashtable对应的键值/// </summary> /// <param name="obj">有效的实体对象</param> /// <returns>包含键值映射的Hashtable</returns> protected overrideHashtable GetHashByEntity(CustomerInfo obj)
{
CustomerInfo info
= obj asCustomerInfo;
Hashtable hash
= newHashtable();

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);returnhash;
}

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

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

namespaceWHC.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= newAppConfig();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,也能够构造出强类型的列表或者对象。因此,合理利用泛型,能够是我们的开发体验更加美好,更加高效。

标签: none

添加新评论