在个人重构之前,在三层学习之后,加入七层的跨越连接,整体感觉还是不错的。
【1】先看下包图:其实在七层敲完后才感觉一目了然,,,
当时感觉包图好宏观,却不清楚内部层与层之间的关系。所以我用下面一张图,来说明我对七层的理解。
当然这里我加入了SqlHelper类,所以,称之为七层,八层也是可以理解的。
用这样一段话来描述这幅图:
U层:接受用户传来的数据,传给外观,再由外观传递给B层判断用户的实际性。而在B层逻辑判断的过程中,要用到抽象工厂加反射技术来判断具体应该实例化的DAL类,以及用到抽象工厂返回的接口,换句话说,这个接口类就是对D层的一个封装,具体的接口实现还是由D层实现,然后返回给B层进行逻辑判断。而sqlhelper的加入,是将D层对数据库的增删改查的过程进行了封装,这样直接由D层传递参数进去,返回的就是相应的查询结果。
下面以具体代码来详细了解。(这里从B层开始,因为U曾和外观都很容易理解)
B层:
<span style="font-family:SimSun;font-size:18px;">Public Class loginBLL
'检查用户名是否存在
Public Function IsExists(ByVal user As Entity.LoginUserInfo) As DataTable
Dim factory As New Factory.LoginFactory()
Dim Iuser As IDAL.IuserinfoDAL
'实例化接口为:已经通过抽象工厂+反射选择数据库后的DAL层。
Iuser = factory.CreateUserInfo()
Dim table As DataTable
table = Iuser.selectUser(user) '通过该DAL层返回table,也就是说这个接口方法的实现是通过该DAL层实现的。
Return table
End Function</span>
配置文件:
<span style="font-family:SimSun;font-size:18px;"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="ConnStr" value ="Server=jialimin;Initial Catalog=Login;User ID=sa;Pwd=1"></add>
<add key ="DB" value="DAL"/>
</appSettings>
</configuration></span>
抽象工厂+反射:
<span style="font-family:SimSun;font-size:18px;">Imports System.Configuration '添加对配置文件的引用
Imports System.Reflection '添加对反射的引用
Imports IDAL
'抽象工厂负责数据库的选择和创建接口
'我理解的抽象工厂加反射目的:通过字符串赋值来选择不同数据库的DAL层。
Public Class LoginFactory
'选择数据库,通过读配置文件给DB字符串赋值。
Dim strDB As String = System.Configuration.ConfigurationSettings.AppSettings("DB")
Public Function CreateUserInfo() As IuserinfoDAL
' 反射的写法:
'objType=Assembly.Load(AssemblyPath).CreateInstance(className)
'其中:AssemblyPath指程序集名。className指命名空间.类名称。
Dim Iuser As IuserinfoDAL
Iuser = CType(Assembly.Load("loginDAL").CreateInstance("loginDAL" & "." & "LoginUserDAL"), IuserinfoDAL)
Return Iuser
End Function</span>
接口层:
<span style="font-family:SimSun;font-size:18px;">Imports Entity
Public Interface IuserinfoDAL
Function selectUser(ByVal user As Entity.LoginUserInfo)
Function UpdateScore(ByVal Score As Entity.LoginScoreInfo)
End Interface</span>
D层:
<span style="font-family:SimSun;font-size:18px;">'system.data.sqlclient命名空间是sqlserver的.net framework 数据提供程序。
Imports System.Data.SqlClient
Imports Entity
Imports IDAL
Imports SQLHelper
Public Class LoginUserDAL : Implements IDAL.IuserinfoDAL
'定义一个sqlhelper对象。
Private SqlHelper As SQLHelper.sqlHelper = New SQLHelper.sqlHelper()
'实现接口的selectuser方法。
Public Function selectUser(user As LoginUserInfo) As Object Implements IuserinfoDAL.selectUser
Dim sql As String
Dim table As DataTable
Dim sqlParams() As SqlParameter = {New SqlParameter("@username", user.UserName), New SqlParameter("@password", user.Password)}
sql = "select * from [User] where UserName=@UserName and Password =@password"
table = SqlHelper.GetDataTable(sql, CommandType.Text, sqlParams)
Return table
End Function
</span>
SqlHelper层:
<span style="font-family:SimSun;font-size:18px;">Imports System.Data.SqlClient
Imports System.Configuration
Imports System.Data
'''其sqlHelper类中的属性和方法不可继承或实例化
Public Class sqlHelper
'获取当前应用程序默认配置文件中的 AppSettingsSection 数据,ConnStr该字符串就是配置文件中设置连接数据库的那部分。
Public Shared connectionString As String = ConfigurationManager.AppSettings("ConnStr")
''' <summary>
'''执行带参数的查询方式,返回值为表。
''' <param name="cmdTxt" >参数cmdText为所要执行的sql语句</param >
''' <param name=" cmdType">查询时的查询方式</param>
''' <param name="paras" >查询时的命令参数</param>
''' <returns >查询后以表的形式返回,</returns >
''' </summary>
''' <remarks></remarks>
Public Shared Function GetDataTable(ByVal cmdTxt As String, ByVal cmdType As CommandType, ByVal paras As SqlParameter()) As DataTable
Dim conn As SqlConnection = New SqlConnection(connectionString) '创建数据库的连接
Dim cmd As SqlCommand '定义命名变量
Dim adataset As DataSet '定义数据适配器,DataSet类表示一个存放于内存中的数据缓存
Dim adaptor As SqlDataAdapter 'SqlDataAdapter类目的是填充DataSet
cmd = New SqlCommand(cmdTxt, conn) '在conn上面执行实例化命令变量,并执行语句cmdtype
cmd.CommandType = cmdType '命令执行的类型
cmd.Parameters.AddRange(paras) '命令执行的参数
adaptor = New SqlDataAdapter(cmd) '初始化 SqlDataAdapter 类的新实例,用指定的 cmd 作为 SelectCommand 的属性
adataset = New DataSet
Try
If conn.State = ConnectionState.Closed Then
conn.Open()
End If
adaptor.Fill(adataset) '向adaptor对象中填充查询的数据
Catch ex As Exception
MsgBox(ex.Message, , "数据库操作")
Finally
If conn.State = ConnectionState.Open Then
conn.Close()
End If
End Try
Return adataset.Tables(0) '获取包含在 DataSet 中的表的集合。
End Function</span>
【2】我认为的难点:
1·配置文件的两个参数:“ConnStr”和“DB”
conntr:
该字符串的值就是设置连接数据库的。最后作为参数传递给SqlHelper类的connectionString
字符串。(如图所示)
DB:
该字符串的值用于数据库的选择。最后作为参数传递给抽象工厂类的strDB字符串。(如果所示)
2· 反射的用法:
objType = Assembly.Load(AssemblyPath).CreateInstance(className)
其中:AssemblyPath指程序集名。className指命名空间.类名称。
起初,虽然知道这两个参数,但我实在不明白程序集是哪个?然后那个className又是什么?后来看到社河师哥才有了那么点感觉。
反射的一个原则:
一切皆以UI层的bin文件夹中的dll名称为中心。
原因很简单:
在我们每次重新生成解决方案,程序运行时,程序集的bin文件中会生成所有要加载的程序集的dll文件,(实在不明白,可以在UI层的bin文件中找到dll文件。)而
.net类加载的机制就是默认从本程序集的bin文件中找,所以bin文件夹中一定要有要加载的程序集的dll。所以,UI 层中bin文件夹中dll叫什么名字AssemblyPath就使用什么名字,而className就是默认的D层的命名空间和类名。如果所示:(框出的两部分一样)
【3】遇到的问题:
这个问题就设计到上面说到的“加载dll文件”一事,我的问题是:
dll文件存在,但是加载路径不对。在我的UI层的bin文件夹下没有D层生成的dll文件,那么程序运行时必然会报错,因为它在UI层根本找不到生成的dll文件,
解决方案有两种:1·手动添加(想想都知道,这肯定不是好办法)2·右击D层名称,选择属性,如图所示:改变其生成输出路径为UI层下的bin文件夹的Debug中。