冬Blog

醉心技术、醉心生活
  博客园  :: 首页  :: 新随笔  :: 订阅 订阅  :: 管理

微型项目实践(8):数据访问的实现

Posted on 2008-05-12 16:38  冬冬  阅读(2967)  评论(39编辑  收藏  举报

上一篇文章,我们分析了数据访问是如何定义,以及如何与其它(特别是Business)模块耦合的,今天我们来看一下数据访问(DataAccess)是如何实现的。

system_design

从系统结构图和上一篇“数据访问的定义”中可以知道,数据访问层的任务就是实现IEntityDataAccess和IDatabase接口。由于Linq的出现,我们只需要一个Adapter,将Linq2Sql的类适配到IEntityDataAccess就可以了,其类设计图和文件结构如下如下:

DataAccessDataAccessFile

从图中可以看到,Database类继承与DataContext,而实现了IDatabase接口;EntityDataAccessAdapter使用System.Data.Linq.Table这个Linq的核心类实现了IEntityDataAccess接口。

Database类的代码如下:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Data.Linq;
   4:  using System.Data.Linq.Mapping;
   5:  using System.IO;
   6:  using System.Linq;
   7:  using System.Reflection;
   8:  using System.Text;
   9:   
  10:  using DongBlog.Common;
  11:  using DongBlog.Business;
  12:  using DongBlog.Business.Blogs;
  13:   
  14:  namespace DongBlog.DataAccess
  15:  {
  16:      /// <summary>
  17:      /// 数据库
  18:      /// </summary>
  19:      public class Database : DataContext, IDatabase
  20:      {
  21:          #region Singelton Pattern
  22:   
  23:          private const string MAPPING_SOURCE_RESOURCE_NAME = "Srims.DataAccess.MappingSource.Xml";
  24:          private static MappingSource _MappingSource;
  25:          private static object _Locker = new object();
  26:   
  27:          private Database(string connectionString, MappingSource mappingSource)
  28:              : base(connectionString, mappingSource)
  29:          {
  30:          }
  31:   
  32:          private static MappingSource getMappingSource()
  33:          {
  34:              if (_MappingSource == null)
  35:              {
  36:                  lock (_Locker)
  37:                  {
  38:                      if (_MappingSource == null)
  39:                      {
  40:                          Stream mapingSourceStream = Assembly.GetAssembly(typeof(Database))
                                   .GetManifestResourceStream(MAPPING_SOURCE_RESOURCE_NAME);
  41:                          if (mapingSourceStream == null)
  42:                              throw new System.IO
                                      .InvalidDataException(
"映射文件不存在!您确定已经将其编译属性设置为\"嵌入资源(Embedded Resource)\"?"
);
  43:                          _MappingSource = XmlMappingSource.FromStream(mapingSourceStream);
  44:                      }
  45:                  }
  46:              }
  47:              return _MappingSource;
  48:          }
  49:   
  50:          /// <summary>
  51:          /// 构造新的数据库
  52:          /// </summary>
  53:          /// <param name="connectionString">数据库连接字符串</param>
  54:          /// <returns>数据库实例</returns>
  55:          public static Database New(string connectionString)
  56:          {
  57:              if (string.IsNullOrEmpty(connectionString))
  58:                  throw new ArgumentNullException("connectionString");
  59:   
  60:              return new Database(connectionString, getMappingSource());
  61:          }
  62:   
  63:          #endregion
  64:   
  65:          #region IDatabase Members
  66:   
  67:          /// <summary>
  68:          /// 取得某一个实体的数据访问
  69:          /// </summary>
  70:          /// <typeparam name="T">实体类型</typeparam>
  71:          /// <returns>该实体的数据访问</returns>
  72:          public IEntityDataAccess<T> GetDataAccess<T>() where T : class
  73:          {
  74:              return new EntityDataAccessAdapter<T>(this);
  75:          }
  76:   
  77:          /// <summary>
  78:          /// 提交数据库变更
  79:          /// </summary>
  80:          public void Submit()
  81:          {
  82:              base.SubmitChanges();
  83:          }
  84:   
  85:          #endregion
  86:   
  87:          #region Blogs
  88:   
  89:          public IEntityDataAccess<Blog> Blogs
  90:          {
  91:              get { return new EntityDataAccessAdapter<Blog>(this); }
  92:          }
  93:   
  94:          public IEntityDataAccess<BlogClass> BlogClasses
  95:          {
  96:              get { return new EntityDataAccessAdapter<BlogClass>(this); }
  97:          }
  98:   
  99:          #endregion
 100:      }
 101:  }

这个代码比较长,我们可以把它分为三部分来看,第一部分是实现了一个Singelton模式,用于构造Linq的外部映射文件,涉及的代码如下:

   1:  //定义内嵌XML映射资源文件的位置
   2:  private const string MAPPING_SOURCE_RESOURCE_NAME = "DongBlog.DataAccess.MappingSource.Xml";
   3:  //定义XML映射源
   4:  private static MappingSource _MappingSource;
   5:  //定义锁
   6:  private static object _Locker = new object();
   7:   
   8:  //私有的构造函数
   9:  private Database(string connectionString, MappingSource mappingSource)
  10:      : base(connectionString, mappingSource) { }
  11:   
  12:  //构造Linq映射源
  13:  private static MappingSource getMappingSource()
  14:  {
  15:      if (_MappingSource == null)
  16:      {
  17:          lock (_Locker)
  18:          {
  19:              if (_MappingSource == null)
  20:              {
  21:                  Stream mapingSourceStream = Assembly.GetAssembly(typeof(Database))
                          .GetManifestResourceStream(MAPPING_SOURCE_RESOURCE_NAME);
  22:                  if (mapingSourceStream == null)
  23:                      throw new System.IO
                               .InvalidDataException(
"映射文件不存在!您确定已经将其编译属性设置为\"嵌入资源(Embedded Resource)\"?");
  24:                  _MappingSource = XmlMappingSource.FromStream(mapingSourceStream);
  25:              }
  26:          }
  27:      }
  28:      return _MappingSource;
  29:  }
  30:   
  31:  /// <summary>
  32:  /// 构造新的数据库
  33:  /// </summary>
  34:  /// <param name="connectionString">数据库连接字符串</param>
  35:  /// <returns>数据库实例</returns>
  36:  public static Database New(string connectionString)
  37:  {
  38:      if (string.IsNullOrEmpty(connectionString))
  39:          throw new ArgumentNullException("connectionString");
  40:   
  41:      return new Database(connectionString, getMappingSource());
  42:  }

以上代码通过Database的私有构造函数和共有静态方法保证数据库使用的外部Linq映射源是静态唯一的。其中Database构造函数继承于DataContext的构造函数,该构造函数在Linq中的定义如下:

   1:  //
   2:  // Summary:
   3:  //     Initializes a new instance of the System.Data.Linq.DataContext class by referencing
   4:  //     a file source and a mapping source.
   5:  //
   6:  // Parameters:
   7:  //   fileOrServerOrConnection:
   8:  //     This argument can be any one of the following:The name of a file where a
   9:  //     SQL Server Express database resides.The name of a server where a database
  10:  //     is present. In this case the provider uses the default database for a user.A
  11:  //     complete connection string. LINQ to SQL just passes the string to the provider
  12:  //     without modification.
  13:  //
  14:  //   mapping:
  15:  //     The System.Data.Linq.Mapping.MappingSource.
  16:  public DataContext(string fileOrServerOrConnection, MappingSource mapping);

Linq的映射文件使用嵌入式资源的方式储存(将其文件属性改为Embedded Resource,如下图)。而getMappingSource方法通过加锁的方式使_MappingSource只初始化一次,其中两次使用了if(_MappingSource == null),确保取得锁的过程中的不会出现并发冲突(尽管这种概率极小)。

LinqXmlFile 

Database的第二部分是实现IDatabase接口,涉及代码如下:

   1:  #region IDatabase Members
   2:   
   3:  /// <summary>
   4:  /// 取得某一个实体的数据访问
   5:  /// </summary>
   6:  /// <typeparam name="T">实体类型</typeparam>
   7:  /// <returns>该实体的数据访问</returns>
   8:  public IEntityDataAccess<T> GetDataAccess<T>() where T : class
   9:  {
  10:      return new EntityDataAccessAdapter<T>(this);
  11:  }
  12:   
  13:  /// <summary>
  14:  /// 提交数据库变更
  15:  /// </summary>
  16:  public void Submit()
  17:  {
  18:      base.SubmitChanges();
  19:  }
  20:   
  21:  #endregion
  22:   
  23:  #region Blogs
  24:   
  25:  public IEntityDataAccess<Blog> Blogs
  26:  {
  27:      get { return new EntityDataAccessAdapter<Blog>(this); }
  28:  }
  29:   
  30:  public IEntityDataAccess<BlogClass> BlogClasses
  31:  {
  32:      get { return new EntityDataAccessAdapter<BlogClass>(this); }
  33:  }
  34:   
  35:  #endregion 

可以从代码中看到,对于GetDataAccess<T>的调用,全部委托给了EntityDataAccessAdapter类。至于EntityDataAccessAdapter类,全部是调用System.Data.Linq.Table的方法,看一下代码很容易就可以明白,就不贴出来了。

最后一个问题:Linq的外部映射文件MappingSource.Xml是怎么来的?答案是代码生成。在DongBlog.Test\Util.cs中新加入以下代码用于生成MappingSource.Xml。

   1:  /// <summary>
   2:  /// 构造Linq的XML映射文件
   3:  /// </summary>
   4:  [TestMethod, Description("构造Linq的XML映射文件")]
   5:  public void Util_CreateXmlMappingFile()
   6:  {
   7:      LinqMappingXmlGenerater linqMappingXmlBuilder =
 new LinqMappingXmlGenerater("", "", Gobal.DatabaseConnectionString);
   8:      var typeArray = new System.Type[]{
   9:          typeof(Blog),
  10:          typeof(BlogClass),
  11:      };
  12:      linqMappingXmlBuilder.BuildMappingXml(typeArray, Gobal.LingMappingFile);
  13:  }

这段调用了YD.Data.LinqGenerater.LinqMappingXmlGenerater类用于生成MappingSource.Xml,其实质是使用SqlMetal这个Linq2Sql附带的工具生成外部映射文件,具体情况可以参看YD\Data\LinqGenerater\LinqMappingXmlGenerater.cs。

至此,我们就有了一个完成的数据访问层。下一篇我们就进入UI层的开发,看看我们如何把我们前面写的这些代码整合在一起。

代码下载