代码改变世界

LINQ to SQL活学活用(1):这要打破旧观念

2008-11-04 21:52 李永京 阅读(...) 评论(...) 编辑 收藏

引入

这个系列呢?我想去挖掘LINQ to SQL另外的一面,把LINQ to SQL发挥的淋漓尽致!按照原来.NET2.0时代比较传统的方法,先建立一个数据库,然后拖到DataSet中做一些读取方法啊,或者直接手写一些操作方法啦,最后在数据访问层中调用这些方法,业务逻辑层中业务判断,在页面表现层上就可以光明正大的使用了。呵呵!这个系列就由我带着大家利用LINQ to SQL全新的思维方式来看看另一面!这个系列的目标是构建一个N层程序,要求这个程序可测试、可维护、可复用、可扩展,最大的要求使用LINQ to SQL作为数据访问层。就是这么多了,别的就不要求了。

注意:这是我第一次分析设计,我只想通过这个系列来讨论学习设计方面的东西,当然很多思想还不成熟或者有错误,只是希望通过这个系列来学习构架设计方面的东西,希望大牛们指点,大家拍砖头!

系列文章导航

LINQ to SQL活学活用(1):这要打破旧观念

LINQ to SQL活学活用(2):躲起来别让我看见

LINQ to SQL活学活用(3):嗅出“臭味”烟消云散

LINQ to SQL活学活用(4):监视你的一举一动

系列参考代码下载:LINQ to SQL

程序架构

现在比较经典的架构,看看下面图片。

分层架构

如何实现

在一个N层应用程序中我们如何使用LINQ to SQL呢?这给刚刚入门的朋友的确是个难题,使用LINQ to SQL就是ORM技术,可以很轻松的实现对数据库记录增删查改操作,但是我们如何去“构建它”才更合理,更科学,更好用?这才是我们真正要学习的,使用面向对象的接口、抽象达到这个目的,面向接口编程就是更好的选择,可以更好的维护和测试。

下面一步一步完成这个程序吧,看到标题了吗?这篇是打破旧观念!看看接下来有什么神秘的地方。首先新建一个工程,有两个类库分别为:数据访问层DataAccess和单元测试UnitTest,看看截图:

程序架构

数据访问层

这篇先创建一个数据访问对象,由于使用LINQ to SQL作为ORM,我们新建一个LINQ to SQL类DataAccessEntities.dbml作为数据访问对象DataContext,一切可视化的操作,为了展示,在O/R设计器中新建一个Customer类(数据访问对象),添加CustomerId、FirstName、LastName三个成员属性,并在成员属性的属性窗口修改相应的属性。

为了展示,这里简单描述下我的操作,设置成员属性如下:

  • CustomerId:成员属性的类型Int,属性的名称CustomerId,数据库列的名称CustomerId,数据库列参与表的主键True,插入时在数据库中自动生成值True,指定插入时自动同步属性。
  • FirstName:属性的名称FirstName,数据库列的名称FirstName,其它默认
  • LastName:属性的名称LastName,数据库列的名称LastName,其它默认

好了,简单的数据访问对象创建好了,下面测试了~~

单元测试层

单元测试用于测试用例的成功与失败,在软件开发中起着尤为重要的地位。当然使用单元测试的要求非常严格,这个系列我将严格遵守,我整理的要求如下

1.尽量在断言中提供错误信息描述,这可以很容易的发现你的错误。

2.每个测试完全独立,体现面向对象中的单一职责原则。

3.不要假设数据库中有什么数据或者哪些数据不在数据库中,在每个测试方法前保证数据库为空的。

4.测试时需要的一些原始数据要作为测试的一部分在测试方法前加载到数据库中。

遵守上面的要求,就可能面临以下的麻烦:

  • 在测试前删除每张表的每行数据,PK关联需要另外写删除SQL语句脚本。
  • 业务逻辑层创建了你没有预料到的数据,你不好处理。
  • 如果你测试失败,在数据库中查看数据不在数据库中,没有任何提示信息你不知道系统做了什么。
  • 由于记录被锁定,插入数据不写入数据库,也不能在不同数据库连接中读取。

幸好LINQ to SQL做到了上面的一切,LINQ to SQL的DataContext可以用来管理数据架构,它提供了DatabaseExists()、DeleteDatabase()、CreateDatabase()方法可以很轻松的创建删除数据库。

注意,这个系列我使用NUnit.Framework测试,所以要引用nunit.framework.dll程序集,另外由于代码编写中涉及了这个类库引用System.configuration.dll、System.Data.Linq.dll程序集和Business项目。

1.创建一个测试基类

测试通常是很复杂的,我们创建一个测试基类用于编写一些通用的方法,然后所有测试类都继承这个测试基类,这个基类主要完成以下功能。

第一步:手动配置DataContext连接和日志

在我们的数据访问层仅仅创建了一个数据访问对象,没有和数据库打交道,测试时需要对DataContext连接到数据库。另外为了测试显示详细的信息,我们还要使用DataContext的日志功能。

public string ConnectionString
 {
     get
     {
         if (ConfigurationManager.ConnectionStrings["conn"] == null ||
             String.IsNullOrEmpty(ConfigurationManager.ConnectionStrings["conn"].ConnectionString) == true)
         {
             throw new InvalidOperationException("默认的连接字符串不存在或者为空");
         }
         return ConfigurationManager.ConnectionStrings["conn"].ConnectionString;
     }
 }
 private DataAccessEntitiesDataContext m_dataContext;
 public DataAccessEntitiesDataContext DataContext
 {
     get
     {
         if (m_dataContext == null)
         {
             m_dataContext = new DataAccessEntitiesDataContext(ConnectionString);
             m_dataContext.Log = Console.Out;
         }
         return m_dataContext;
     }
 }

第二步:创建数据库

在测试之前,打开临时连接用于创建数据库,使用DataContext提供了DatabaseExists()、DeleteDatabase()、CreateDatabase()方法,先使用DatabaseExists()验证数据库是否存在,如果存在使用DeleteDatabase()方法删除,使用CreateDatabase()方法创建一个数据库架构,及时释放这个连接。

[TestFixtureSetUp]
public void Init()
{
    DataAccessEntitiesDataContext context = new DataAccessEntitiesDataContext(ConnectionString);
    context.Log = Console.Out;
    if (context.DatabaseExists() == true)
    {
        context.DeleteDatabase();
    }
    context.CreateDatabase();
    context.Connection.Close();
    context.Dispose();
    context = null;
}

第三步:关闭所有连接

在测试结束关闭所有的连接,这一步非常必要哦。

[TestFixtureTearDown]
public void Tear()
{
    DataContext.Connection.Close();
}

第四步:设置连接字符串

新建一App.config文件,设置连接字符串:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="conn" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=LINQ;Integrated Security=True"/>
  </connectionStrings>
</configuration>

2.测试类

我们新建一个测试类用于测试数据访问对象,这里简单测试创建一个Customer对象,新建CustomerFixture.cs类继承UnitTestBase

编写创建Customer对象方法如下,创建一个Customer对象,调用InsertOnSubmit()方法插入,调用DataContext.SubmitChanges()方法提交数据库。

[Test]
public void CreateCustomerTest()
{
    Customer customer = new Customer() { FirstName = "YJing", LastName = "Lee" };
    Assert.AreEqual(0, customer.CustomerId, "测试前CustomerId为0");
    DataContext.Customer.InsertOnSubmit(customer);
    DataContext.SubmitChanges();
    Assert.AreNotEqual(0, customer.CustomerId, "调用SubmitChanges()方法后CustomerId不为0");
}

测试成功,看看输出结果:

测试输出信息

OH!非常酷!首先创建了数据库架构,然后插入了一条数据。再次测试一下这个方法将是什么结果呢?这个问题就留给大家了。

结语

看到了吗?这就是全新的方式来完成非常酷的工作!从面向对象入手,利用LINQ to SQL生成其关系型数据库,一切就是这么容易!这篇仅仅在数据访问层上新建一数据访问对象,在测试中调用DataContext提供的方法完成数据操作!下篇更精彩!