学习三层结构心得(一)
以前分析过Microsoft中另一个轻巧的三层结构的PetShop例子,发现PetShop还是比较好懂。这几天有空一直在看Duwamish的代码,参照了几篇分析Duwamish的文章。所以,这些天都会围绕这两个例子来学习,并写一些心得。
首先,Duwamish分层为五层结构。Web层,业务外观层,业务规则层,业务实体层,数据访问层。
业务实体层,里面实现的是各个实体的对象,用DataSet来表示,比如:Book对象,Custermer对象,Order对象等。建立该层以方便在其它各个层来传递数据(也就是说,各层传递数据是以DataSet来传递的)。另个,这个对象实体也与数据库中的库结构是对应的。
抽取其中的一段代码来分析。以BookData.cs为例,在其中,就建立了Book这个类:
一旦对象实体建立好,其余层皆可创建该类的一个对象用它来作数据传递。
数据访问层:功能很简单,与数据库进行交互。无非就是几种操作:Query,Update,Insert,Delete,当然,要根据数据库的设计,由程序应用提升出来的数据操作抽象来写出怎么与数据库进行哪些操作。还是看代码,看看DataAccess中的Book类怎么进行与数据库操作。
首先,book类继承自IDisposable,它可以派生Dispose函数来自主释放资源(在这里是指释放数据库的链接)。另外,它有一个SqlDataAdapter的成员,就是用来让SqlCommand与DataSet进行互通的,DataSet就是我们上面所提到的业务实体的对象。
不难看出,Book数据层应该对数据库的操作就是按照种类来查找书,按bookid来查找某本书,按作者来查找某些书,按书号来查找书,等等,于是我们在数据库中写入相应的存储过程(GetBooksByCategoryId,GetBookById,GetBooksByAuthor,GetBooksByISBN),然后,在源代码中调用如下:
以上就是数据层做的事。
至于业务规则层与业务外观层,暂时没看到,续...
首先,Duwamish分层为五层结构。Web层,业务外观层,业务规则层,业务实体层,数据访问层。
业务实体层,里面实现的是各个实体的对象,用DataSet来表示,比如:Book对象,Custermer对象,Order对象等。建立该层以方便在其它各个层来传递数据(也就是说,各层传递数据是以DataSet来传递的)。另个,这个对象实体也与数据库中的库结构是对应的。
抽取其中的一段代码来分析。以BookData.cs为例,在其中,就建立了Book这个类:
public class BookData : DataSet
{
public const String BOOKS_TABLE = "Books";
/// <value>The constant used for PKId field in the Books table. </value>
public const String PKID_FIELD = "PKId";
/// <value>The constant used for TypeId field in the Books table. </value>
public const String TYPE_ID_FIELD = "TypeId";
/// <value>The constant used for PublisherId field in the Books table. </value>
public const String PUBLISHER_ID_FIELD = "PublisherId";
/// <value>The constant used for PublicationYear field in the Books table. </value>
public const String PUBLICATION_YEAR_FIELD = "PublicationYear";
/// <value>The constant used for ISBN field in the Books table. </value>
public const String ISBN_FIELD = "ISBN";
/// <value>The constant used for ImageFileSpec field in the Books table. </value>
public const String IMAGE_FILE_SPEC_FIELD = "ImageFileSpec";
/// <value>The constant used for Title field in the Books table. </value>
public const String TITLE_FIELD = "Title";
/// <value>The constant used for Title field in the Books table. </value>
public const String DESCRIPTION_FIELD = "Description";
/// <value>The constant used for UnitPrice field in the Books table. </value>
public const String UNIT_PRICE_FIELD = "UnitPrice";
/// <value>The constant used for UnitCost field in the Books table. </value>
public const String UNIT_COST_FIELD = "UnitCost";
/// <value>The constant used for ItemType field in the Books table. </value>
public const String ITEM_TYPE_FIELD = "ItemType";
/// <value>The constant used for PublisherName field in the Books table. </value>
public const String PUBLISHER_NAME_FIELD = "PublisherName";
/// <value>The constant used for Authors field in the Books table. </value>
public const String AUTHORS_FIELD = "Authors";
///表示按什么来进行查找。有按title,isbn,author等查找方法
[SerializableAttribute]
public enum SearchTypeEnum
{
/// <summary>
/// Title search.
/// </summary>
Title = 0,
/// <summary>
/// ISBN search.
/// </summary>
ISBN = 1,
/// <summary>
/// Author search.
/// </summary>
Author = 2,
/// <summary>
/// Subject search.
/// </summary>
Subject = 3,
/// <summary>
/// Id search.
/// </summary>
ID = 4,
/// <summary>
/// Id list search.
/// </summary>
IdList = 5
}
///这个函数没分析出它什么意思
private BookData(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
///构造函数,在类对象中加入一张表
public BookData()
{
//
// Create the tables in the dataset
//
BuildDataTables();
}
///建立表结构,并加入DataSet
private void BuildDataTables()
{
//
// Create the Books table
//
DataTable table = new DataTable(BOOKS_TABLE);
DataColumnCollection columns = table.Columns;
columns.Add(PKID_FIELD, typeof(System.Int32));
columns.Add(TYPE_ID_FIELD, typeof(System.Int32));
columns.Add(PUBLISHER_ID_FIELD, typeof(System.Int32));
columns.Add(PUBLICATION_YEAR_FIELD, typeof(System.Int16));
columns.Add(ISBN_FIELD, typeof(System.String));
columns.Add(IMAGE_FILE_SPEC_FIELD, typeof(System.String));
columns.Add(TITLE_FIELD, typeof(System.String));
columns.Add(DESCRIPTION_FIELD, typeof(System.String));
columns.Add(UNIT_PRICE_FIELD, typeof(System.Decimal));
columns.Add(UNIT_COST_FIELD, typeof(System.Decimal));
columns.Add(ITEM_TYPE_FIELD, typeof(System.String));
columns.Add(PUBLISHER_NAME_FIELD, typeof(System.String));
//
// [Authors] is an optional column that will get added dynamically to the table schema
// when the stored proc. GetBookById is used to fill the 'Books' table
//
this.Tables.Add(table);
}
}
{
public const String BOOKS_TABLE = "Books";
/// <value>The constant used for PKId field in the Books table. </value>
public const String PKID_FIELD = "PKId";
/// <value>The constant used for TypeId field in the Books table. </value>
public const String TYPE_ID_FIELD = "TypeId";
/// <value>The constant used for PublisherId field in the Books table. </value>
public const String PUBLISHER_ID_FIELD = "PublisherId";
/// <value>The constant used for PublicationYear field in the Books table. </value>
public const String PUBLICATION_YEAR_FIELD = "PublicationYear";
/// <value>The constant used for ISBN field in the Books table. </value>
public const String ISBN_FIELD = "ISBN";
/// <value>The constant used for ImageFileSpec field in the Books table. </value>
public const String IMAGE_FILE_SPEC_FIELD = "ImageFileSpec";
/// <value>The constant used for Title field in the Books table. </value>
public const String TITLE_FIELD = "Title";
/// <value>The constant used for Title field in the Books table. </value>
public const String DESCRIPTION_FIELD = "Description";
/// <value>The constant used for UnitPrice field in the Books table. </value>
public const String UNIT_PRICE_FIELD = "UnitPrice";
/// <value>The constant used for UnitCost field in the Books table. </value>
public const String UNIT_COST_FIELD = "UnitCost";
/// <value>The constant used for ItemType field in the Books table. </value>
public const String ITEM_TYPE_FIELD = "ItemType";
/// <value>The constant used for PublisherName field in the Books table. </value>
public const String PUBLISHER_NAME_FIELD = "PublisherName";
/// <value>The constant used for Authors field in the Books table. </value>
public const String AUTHORS_FIELD = "Authors";
///表示按什么来进行查找。有按title,isbn,author等查找方法
[SerializableAttribute]
public enum SearchTypeEnum
{
/// <summary>
/// Title search.
/// </summary>
Title = 0,
/// <summary>
/// ISBN search.
/// </summary>
ISBN = 1,
/// <summary>
/// Author search.
/// </summary>
Author = 2,
/// <summary>
/// Subject search.
/// </summary>
Subject = 3,
/// <summary>
/// Id search.
/// </summary>
ID = 4,
/// <summary>
/// Id list search.
/// </summary>
IdList = 5
}
///这个函数没分析出它什么意思
private BookData(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
///构造函数,在类对象中加入一张表
public BookData()
{
//
// Create the tables in the dataset
//
BuildDataTables();
}
///建立表结构,并加入DataSet
private void BuildDataTables()
{
//
// Create the Books table
//
DataTable table = new DataTable(BOOKS_TABLE);
DataColumnCollection columns = table.Columns;
columns.Add(PKID_FIELD, typeof(System.Int32));
columns.Add(TYPE_ID_FIELD, typeof(System.Int32));
columns.Add(PUBLISHER_ID_FIELD, typeof(System.Int32));
columns.Add(PUBLICATION_YEAR_FIELD, typeof(System.Int16));
columns.Add(ISBN_FIELD, typeof(System.String));
columns.Add(IMAGE_FILE_SPEC_FIELD, typeof(System.String));
columns.Add(TITLE_FIELD, typeof(System.String));
columns.Add(DESCRIPTION_FIELD, typeof(System.String));
columns.Add(UNIT_PRICE_FIELD, typeof(System.Decimal));
columns.Add(UNIT_COST_FIELD, typeof(System.Decimal));
columns.Add(ITEM_TYPE_FIELD, typeof(System.String));
columns.Add(PUBLISHER_NAME_FIELD, typeof(System.String));
//
// [Authors] is an optional column that will get added dynamically to the table schema
// when the stored proc. GetBookById is used to fill the 'Books' table
//
this.Tables.Add(table);
}
}
一旦对象实体建立好,其余层皆可创建该类的一个对象用它来作数据传递。
数据访问层:功能很简单,与数据库进行交互。无非就是几种操作:Query,Update,Insert,Delete,当然,要根据数据库的设计,由程序应用提升出来的数据操作抽象来写出怎么与数据库进行哪些操作。还是看代码,看看DataAccess中的Book类怎么进行与数据库操作。
public class Books : IDisposable
{
private SqlDataAdapter dsCommand;
public Books()
{
dsCommand = new SqlDataAdapter();
dsCommand.SelectCommand = new SqlCommand();
dsCommand.SelectCommand.Connection = new SqlConnection(DuwamishConfiguration.ConnectionString);
dsCommand.TableMappings.Add("Table", BookData.BOOKS_TABLE);
}
/// <summary>
/// Dispose of this object's resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(true); // as a service to those who might inherit from us
}
/// <summary>
/// Free the instance variables of this object.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (! disposing)
return; // we're being collected, so let the GC take care of this object
if (dsCommand != null )
{
if (dsCommand.SelectCommand != null)
{
if( dsCommand.SelectCommand.Connection != null)
dsCommand.SelectCommand.Connection.Dispose();
dsCommand.SelectCommand.Dispose();
}
dsCommand.Dispose();
dsCommand = null;
}
}
}
{
private SqlDataAdapter dsCommand;
public Books()
{
dsCommand = new SqlDataAdapter();
dsCommand.SelectCommand = new SqlCommand();
dsCommand.SelectCommand.Connection = new SqlConnection(DuwamishConfiguration.ConnectionString);
dsCommand.TableMappings.Add("Table", BookData.BOOKS_TABLE);
}
/// <summary>
/// Dispose of this object's resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(true); // as a service to those who might inherit from us
}
/// <summary>
/// Free the instance variables of this object.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (! disposing)
return; // we're being collected, so let the GC take care of this object
if (dsCommand != null )
{
if (dsCommand.SelectCommand != null)
{
if( dsCommand.SelectCommand.Connection != null)
dsCommand.SelectCommand.Connection.Dispose();
dsCommand.SelectCommand.Dispose();
}
dsCommand.Dispose();
dsCommand = null;
}
}
}
首先,book类继承自IDisposable,它可以派生Dispose函数来自主释放资源(在这里是指释放数据库的链接)。另外,它有一个SqlDataAdapter的成员,就是用来让SqlCommand与DataSet进行互通的,DataSet就是我们上面所提到的业务实体的对象。
不难看出,Book数据层应该对数据库的操作就是按照种类来查找书,按bookid来查找某本书,按作者来查找某些书,按书号来查找书,等等,于是我们在数据库中写入相应的存储过程(GetBooksByCategoryId,GetBookById,GetBooksByAuthor,GetBooksByISBN),然后,在源代码中调用如下:
public BookData GetBooksByCategoryId(int categoryId)
{
return FillBookData("GetBooksByCategoryId", "@CategoryId", categoryId.ToString());
}
public BookData GetBookById(int bookId)
{
return FillBookData("GetBookById", "@BookId", bookId.ToString());
}
public BookData GetBooksByAuthor(String searchText)
{
return FillBookData("GetBooksByAuthor", "@Author", searchText );
}
public BookData GetBooksByISBN(String searchText)
{
return FillBookData("GetBooksByISBN", "@ISBN", searchText);
}
private BookData FillBookData(String commandText, String paramName, String paramValue)
{
if (dsCommand == null )
{
throw new System.ObjectDisposedException( GetType().FullName );
}
BookData data = new BookData();
SqlCommand command = dsCommand.SelectCommand;
command.CommandText = commandText;
command.CommandType = CommandType.StoredProcedure; // use stored proc for perf
SqlParameter param = new SqlParameter(paramName, SqlDbType.NVarChar, 255);
param.Value = paramValue;
command.Parameters.Add(param);
dsCommand.Fill(data);
return data;
}
{
return FillBookData("GetBooksByCategoryId", "@CategoryId", categoryId.ToString());
}
public BookData GetBookById(int bookId)
{
return FillBookData("GetBookById", "@BookId", bookId.ToString());
}
public BookData GetBooksByAuthor(String searchText)
{
return FillBookData("GetBooksByAuthor", "@Author", searchText );
}
public BookData GetBooksByISBN(String searchText)
{
return FillBookData("GetBooksByISBN", "@ISBN", searchText);
}
private BookData FillBookData(String commandText, String paramName, String paramValue)
{
if (dsCommand == null )
{
throw new System.ObjectDisposedException( GetType().FullName );
}
BookData data = new BookData();
SqlCommand command = dsCommand.SelectCommand;
command.CommandText = commandText;
command.CommandType = CommandType.StoredProcedure; // use stored proc for perf
SqlParameter param = new SqlParameter(paramName, SqlDbType.NVarChar, 255);
param.Value = paramValue;
command.Parameters.Add(param);
dsCommand.Fill(data);
return data;
}
以上就是数据层做的事。
至于业务规则层与业务外观层,暂时没看到,续...