前言
.netTiers是针对对象关系映射的一个 CodeSmith模板,它采用现成的SQL Server数据库,并为你的应用程序自动生成个性化的DataTiers应用程序块。

 主要的特性有:

  • 产生现成的Visual Studio项目和解决方案
  • 完全集成企业库应用程序块框架,用自生插件封装,因而你可以直接用企业 库配置控制台来配置你的应用程序
  • 一对一地产生业务对象(实体),一个实体对应一个表,一个属性对应一列
  • 可序列化
  • 触发实体事件
  • 实现了接口IEntity,包含了表中出现的每一个列
  • 对枚举类型及其产生的特别支持
  • 为数据表和视图产生数据访问层组件,来完成 以下数据库操作:
  • 基 本的CRUD:UPDATE,DELETE,INSERT,SELECT ALL,PAGED SELECT,FIND
  • 按主键查询
  • 按外键查询
  • 按索引查询(非主键和外键)
  • 按连接查询(多对多关系)
  • 支持由命名规则确定的查询产生的用户自定义方法。你也可以从邮件中获 取更多的信息
  • 使用子 查询和可选的嵌套来完成高级读取和保存
  • 支持分页和排序的查询方法
  • 支持SQL视图
  • 可以选择存储过程或者使用嵌入XML查询文件的参数化SQL语句
  • 使用TList<>和(或)VList为实体和库产生强 类型的通用集合。
  • 实 现了 BingdingList<T>,IBindingListView,IBindingList,IList,ICloneable,IListSource,ITypedList,IDisposable,IComponent,IRaiseITemChangedEvents,IDeserializationCallback 等泛型和接口
  • 可排序 的,甚至不可排序
  • 可 以绑定任意的gridview,datagrid或者任何其他winform和asp.net控件
  • 为分布式编程创建ASP.NET Webservice
  • 创建存储过程脚本并且能自动的安装到服务器
  • 创建完整的NAnt build文件来编译,测试和产生chm/html API文档
  • 创建基于数据库方案的全套验证规则库,并且包括管理你的规则的完整框架
  • 每个对象有其具体类及其继承的基类。这个具体的类一旦被产生,你可以 在自己的代码里面使用。每个基类都是分布类型类,你可以毫无障碍地扩展其功能
  • 创建一个EntityDataSource,你将不再被 ObjectDataSource的缺点所困扰,.netTiers提供了先进的EntityDataSource,它关联数据仓库并创建100%公开的 模型,没有任何后台代码
  • 创建全套的web管理控件,你可以为你的数据库准确快速地建立一个web管理平台
  •  Do I have to keep going, I mean come on, there are features out the wazzuua, download it today...
  • 全套的NUnit测试
  • 代码有详细的注解来描述数据表及其列的扩展 属性,遵循微软命名规则

它是免费和开源的!你可以在我们的允 许条款内随意的修改模板,捐献出来回馈社区(http://www.sourceforge.net/projects/nettiers
 
第一章      概念
这个data tiers的概念包括自定义业务实体组件(数据本身)和数据访问逻辑组件(持久逻辑)两部分。设计的灵感来自于微软的 patterns&practices向导Designing Data Tier Components and Passing Data Through Tiers:



第二章      Northwind示例
示例模型
为了阐明模板的原理,我们使用了Employee表:
表2.1.  Employee表

产生的OO架构如下:
(点击查看原图)
代码示例:

using Northwind.DataAccessLayer;
// 获取所有的employee,并以LastName升序排序输出
TList<Employees> employees = DataRepository.EmployeesProvider.GetAll();
employees.Sort(EmployeeColumns.LastName, ListSortDirection.Ascending);
foreach(Employees employee in employees)
{
    Console.WriteLine(
"{1} {0}", employee.FirstName, employee.LastName);
}



using Northwind.DataAccessLayer;
// 创建并保存一条记录
Employees employee = new Employees();
employee.FirstName 
= "John";employee.LastName = "Doe";
employee.BirthDate 
= DateTime.Now;employee.Address = "10 , fake street";
employee.City 
= "Metroplolis";employee.Country = "USA";
employee.HireDate 
= DateTime.Now;
employee.HomePhone 
= "0123456789";
employee.Notes 
= "This is a fake employee";
employee.Title 
= "M";
employee.TitleOfCourtesy 
= "Dear";
employee.PostalCode 
= "5556";
employee.Region 
= "New-York";
DataRepository.EmployeeProvider.Insert(employee); 

//看,新的ID已经增加
Console.WriteLine("New Employee ID" + employee.EmployeeID); 

using Northwind.DataAccessLayer;
// 按主键查询和删除 

// 示范insert,update,delete方法也可以采用collection作 为参数
Employee employee = SqlDataRepository.EmployeeProvider.GetByEmployeeID(13);
DataRepository.EmployeesProvider.Delete(employees);

using Northwind.DataAccessLayer;
// SqlClient可以使用事务处理
// 显示封装了insert,update,delete方法的Save方法
TransactionManager transaction = DataRepository.CreateTransaction();
transaction.BeginTransaction(
/**//*IsolationLevel.ReadUncommited*/);
try
{
    
// 新增
    Employee employee = new Employee();
    employee.FirstName 
= "John";
    employee.LastName 
= "Doe";
    employee.BirthDate 
= DateTime.Now;
    employee.Address 
= "10 , fake street";
    employee.City 
= "Metroplolis";
    employee.Country 
= "USA";    
    employee.HireDate 
= DateTime.Now;
    employee.HomePhone 
= "0123456789";
    mployee.Notes 
= "This is a fake employee";
    employee.Title 
= "M";
    employee.TitleOfCourtesy 
= "Doctor";
    employee.PostalCode 
= "5556";
    employee.Region 
= "New-York";
    DataRepository.EmployeeProvider.Save(transaction, employee);   

    
// 修改employee实例
    employee.Notes = "This is a modified fake employee";    

    
// 更新
    DataRepository.EmployeeProvider.Save(transaction, employee); 
    transaction.Commit();
    Console.WriteLine(
"ok");
}

catch(Exception ex)
{
    
try { transaction.Rollback();} catch(){}
    Console.WriteLine(
"nok : {0}", ex);
}
    

/**//*
    DeepSave辅助方法能协助你以一次调用来保存对象及其成员
*/


using Northwind.DataAccessLayer;

Order order 
= Order.CreateOrder("ALFKI"1, DateTime.Now, DateTime.Now, DateTime.Now, 10.1m"ship name""ship address" , "paris""idf""75000""france");
order.OrderDetailCollection.Add(order.OrderID, 
115.6m100.02f);
order.OrderDetailCollection.Add(order.OrderID, 
2122.6m430.03f);
DataRepository.OrderProvider.DeepSave(order);
Console.WriteLine(
"new order saved: orderId is: " + order.OrderID.ToString());

/**//*
你也可以在配置控制台配置多个数据提供者,用代码来使用其中不是默认的的一个
*/

using Northwind.DataAccessLayer;

SqlDataProvider myRepository 
= DataRepository.Providers["my second data provider"as Northwind.DataAccessLayer.SqlClient.SqlDataProvider;
this.listBox1.DataSource = myRepository.ProductProvider.GetAll();
this.listBox1.DisplayMember = "ProductName";
this.listBox1.ValueMember = "ProductID"

//要不然,如果你不能预先配置,你也可以在运行时改变连接字符串

using Northwind.DataAccessLayer;
//使用配置的其他连接字符串的新语法
TList<Products> list = DataRepository.Connections["NorthwindConnectionString2"].Provider.CustomersProvider.GetAll(); 

//使用动态的连接字符串的新语法
DataRepository.AddConnection("DynamicConnectionString""Data Source=(local);Initial Catalog=Northwind;Integrated Security=true;");
TList
<Products> list = DataRepository.Connections["DynamicConnectionString"].Provider.ProductsProvider.GetAll();   
this.listBox1.DataSource = list;
this.listBox1.DisplayMember = "ProductName";
this.listBox1.ValueMember = "ProductID";

 

第三章      配置

以下是配置.netTiers组件的步骤,以Northwind数据库为例。

       为了配置你的应用程序来使用.netTiers,你需要增加以下的节到你的App/Web config文件中。在.netTiers 2 Install and Configuration Document你能找到更多的信息。

1.       增加一个新的节到configSettings

<section name="netTiersService"
    type
="Northwind.DataAccessLayer.Bases.NetTiersServiceSection, Northwind.DataAccessLayer"
        allowDefinition
="MachineToApplication"
        restartOnExternalChanges
="true" />

2.       在ConnectionStrings节中增加一项

<connectionStrings>
  
<add name="netTiersConnectionString" connectionString="Data Source=(local);Initial Catalog=Northwind;Integrated Security=true;Connection Timeout=1;" />
</connectionStrings>
3.    增加netTierServie配置节到你的配置文件中。不使用的时候注释掉即可。
<netTiersService defaultProvider="SqlNetTiersProvider">
  
<providers>
    
    
<add 
        
name="SqlNetTiersProvider" 
        type
="Northwind.DataAccessLayer.SqlClient.SqlNetTiersProvider, Northwind.DataAccessLayer.SqlClient"
        connectionStringName
="netTiersConnectionString"
        useStoredProcedure
="false"
        providerInvariantName
="System.Data.SqlClient" />
    
  
</providers>
</netTiersService>
  第四章      高级读取和保存

   第五章      增加基于存储过程的数据访问方法
 

之后我们运行产 生器,将看到SqlClientProductRepository类的自定义方法(Custom Methods)如下:

/// <summary>
///    This method wrap the '_Products_GetWithStockBelow' stored procedure. 
/// </summary>
/// <param name="UnitsInStock"> A <c>System.Int16</c> instance.</param>
/// <param name="start">Row number at which to start reading.</param>
/// <param name="pageLength">Number of rows to return.</param>
/// <remark>This method is generate from a stored procedure.</remark>
/// <returns><see cref="ProductCollection" /> instance.</returns>

public  ProductCollection GetWithStockBelow(int start, int pageLength , System.Int16 UnitsInStock)
{
    
return GetWithStockBelow(this.transactionManager, this.connectionString, 0int.MaxValue , UnitsInStock);
}


/// <summary>
///    This method wrap the '_Products_GetWithStockBelow' stored procedure. 
/// </summary>
/// <param name="UnitsInStock"> A <c>System.Int16</c> instance.</param>
/// <param name="transactionManager"><see cref="TransactionManager" /> object</param>
/// <remark>This method is generate from a stored procedure.</remark>
/// <returns><see cref="ProductCollection" /> instance.</returns>

public  ProductCollection GetWithStockBelow(TransactionManager transactionManager , System.Int16 UnitsInStock)
{
    
return GetWithStockBelow(transactionManager, string.Empty , 0int.MaxValue , UnitsInStock);
}


/// <summary>
///    This method wrap the '_Products_GetWithStockBelow' stored procedure. 
/// </summary>
/// <param name="UnitsInStock"> A <c>System.Int16</c> instance.</param>
/// <remark>This method is generate from a stored procedure.</remark>
/// <returns><see cref="ProductCollection" /> instance.</returns>

public  ProductCollection GetWithStockBelow(System.Int16 UnitsInStock)
{
    
return GetWithStockBelow(this.transactionManager, this.connectionString, 0int.MaxValue , UnitsInStock);
}


    
/// <summary>
///    This method wrap the '_Products_GetWithStockBelow' stored procedure. 
/// </summary>
/// <param name="UnitsInStock"> A <c>System.Int16</c> instance.</param>
/// <param name="start">Row number at which to start reading.</param>
/// <param name="pageLength">Number of rows to return.</param>
/// <param name="transactionManager"><see cref="TransactionManager" /> object</param>
/// <param name="connectionString">Connection string to datasource.</param>
/// <remark>This method is generate from a stored procedure.</remark>
/// <returns><see cref="ProductCollection" /> instance.</returns>

protected  ProductCollection GetWithStockBelow(TransactionManager transactionManager, string connectionString, int start, int pageLength , System.Int16 UnitsInStock)
{
    SqlDataReader reader;
    
if (transactionManager != null)
        reader 
= SqlHelper.ExecuteReader(transactionManager.TransactionObject, "_Products_GetWithStockBelow", UnitsInStock);
    
else
        reader 
= SqlHelper.ExecuteReader(connectionString, "_Products_GetWithStockBelow", UnitsInStock);
    
    
//Create Collection
    ProductCollection rows = new ProductCollection();
    Fill(reader, rows, start, pageLength);
    reader.Close();
    
return rows;
}


 

正如你看到的,所有重载方法被产生,包括SqlClientWebServiceClient版本并且支持参数和分页。
 

第六章 NAnt文件

模板将产生一个NAntBuild文件。这里我不解释什么是NAnt了,你可以从nAnt homepage找到更多的信息。基本上,它是用于在没有visual studio的情况下从命令行编译程序,但是它又不仅仅局限于此,它也可以用于运行Nunit测试和建立NDoc文档。
(点击查看原图)
 

第七章 模板参数

本模板在许多方面是可设定的:

DataSource

SourceDatabase

你想创建应用程序块的数据库

SourceTables

你想包含到产生器内的数据表列表

EntireDatabase

指明是否所有数据表必须被包含, 结果是它将覆盖SourceTables列表

General

OuputDirectory

产生代 码的输出路径

BusinessLogicLayerFolderName

BLL(实 体和实体关系)代码输出文件夹名,推荐为空

DataAccessLayerFolderName

DAL(数 据仓库)代码输出文件夹名,推荐为:DataAccessLayer

SqlFolderName

数据库 存储过程脚本的输出文件夹名,推荐为:SQL

NameSpace

项目的根 命名空间。DAL的输出文件夹名将作为DAL的命名空间(命名空间结构即为目录结构)

GenerateUnitTest

指明是 否产生测试代码

VsNetIntegration

说明 Visual Studio集成的类型:none,single project,separated project(BLL和DAL)

VsNetVersion

说明Visual Studio的版本

WebService参数

GenerateWebService

指明是 否asp.net webservice及其对应的DAL必须产生

WebServiceOutputPath

asp.net webservice的完整输出路径

WebServiceUrl

WebService 输出路径所对应的URL

(例如:http://localhost/Services/DatabaseNameRepository


第八章 单元测试

本模板能可选地产生NUnit测试代码。目前为止,产生的代码包括:

  • Insert
  • Update
  • GetAll
  • Delete
  • DeepLoad
  • Serialization

(点击查看原图)
 

第九章 建 议

数据库设计指南

理想的情况下,为了模板最好的实现,你的数据库应该应用以下的规则:

  • 表名是单一的并且使用Pascal命名方法
  • 字段使用Pascal命名方法并且给出相应的描述
  • 外键关联
  • 要 作为查询条件的一个字段或多个字段应该是表的索引
  • 自定义的存储过程名 应该以一个下划线开始,后面接表名
  • 表名不要以“Collection”结尾
  • 不要以“ListItem”作为表名

第十章 版本历史

v0.9 - Childebert

v1.0 - Clotaire

v1.1 - Caribert

 

第十二章 项目信息

描述

本项目是开源的,由sourceforge主持。你也可以通过搜索或者codesmith论坛来获取信息。模板最早是由Ryan Hurdon开发的,现在有John Roland维护。

(贡献人等信息略)

注:在 使用时发现文中的代码有些函数存在问题,变量也命名有出入,不知道作者是由于什么原因造成的。

tag: .net nettiers O/R Mapping codesmith

 

概述

你应该增加数据访问组件的新方法到具体的类中,而不是每一次产生类的时候重写基类,具体的类仅仅产 生一次。这种解决方案是好的,但是目前有一些缺陷:

l         为了提供相同的功能你必须重载许多方法,而不是产生代码
可选的解决方法是:从你的存储过程产生方法。为了激活它,你需要设定“Include customsql”选项为True。这样,你的存储过程名必须以“_TableName_”开始,后面接上你给方法取的名称。这个命名规范保证模板能检 测到你的存储过程,从而为正确的数据类(xxTableNameRepository)创建方法。

示例
用一个例子作为解释,考虑Northwind数据库的Product数据表,假设我们需要查询库存在给出值以下的所有产品列 表,那么首先我们创建以下存储过程:

-- Get the products that have less units in stock than the @UnitsInStock parameter.
CREATE PROCEDURE dbo._Products_GetWithStockBelow
@UnitsInStock smallint
AS

SELECT
[ProductID],
[ProductName],
[SupplierID],
[CategoryID],
[QuantityPerUnit],
[UnitPrice],
[UnitsInStock],
[UnitsOnOrder],
[ReorderLevel],
[Discontinued]
FROM
[dbo].[Products]
WHERE
[UnitsInStock] 
< @UnitsInStock
GO