Active Record是业务逻辑层中(《企业应用架构模式》将该模式归为数据源模式)常用的一种框架模式,尤其在底层数据库模型匹配业务模型时它特别有用,它是一种以数据库驱动为主的框架模式。
通常,数据库中的每张表都对应一个业务对象。业务对象表示表中的一行,并且包含数据、行为以及持久化该对象的工具,此外还有添加新实例和查找对象集合所需的方法。
在Active Record模式中,每个业务对象均负责自己的持久化和相关的业务逻辑。
Active Record模式非常适用于在数据模型与业务模型之间具有 一对一映射关系的简单应用程序,如博客或论坛。
因为业务对象与数据库中的表具有一对一映射关系,而且均具有相同的CRUD方法,所以可以使用代码生成器自动生成业务模型。
Active Record模式中的对象通常会包含用来执行数据库操作的CRUD操作,还有相关验证及业务相关的计算和检查功能。
特别地,典型的Active Record类还包含一些表示数据表中的列的实例属性和操作于当前对象上的实例方法。Active Record类中还可能包含一些静态方法,用来操作数据表上的所有记录。(下面的实战操作可能会很好的帮助你理解这句话)
Active Record的成功依赖于两个因素:简单和框架。Active Record概念上理解起来十分的简单, 不过若手工实现,仍然需要很多代码。
编写并维护每一个类都需要大量的代码,不过这仅仅是最低的需求,因为你可能还要考虑为每个类或者数据表添加一个或多个数据迁移对象(DTO)。
不可否认,活动记录确实在简单和最终系统的强大方面找到了良好的平衡。且该模式也等到了很多开发商的支持,例如,LINQ-to-SQL和我们今天的主角--Castle ActiveRecord。
理论上,开发中还需要一个额外的层存在于对象模型和数据模式之间,通常叫做数据映射层(Data Mapper)。在Active Record中,我们可以将该层直接集成在对象中,不过随着这一层越来越复杂,维护成本也在逐渐提高。
Active Record的另外一个问题是对象和数据表设计之间的绑定。若你不得不修改数据库,那么要同时修改Active Record的对象模型以及所有的相关代码。
表模式通常就是目前三层框架中流行的以DateTable和DateSet为载体在各层中传递数据,Active Record则是以对象模型、泛型(如IList<Post>)为载体在各层中传递数据。
Castle ActiveRecord 是ActiveRecord 模式的一个实现,Castle ActiveRecord依赖Nhibernate来完成实际的映像。与单纯的ActiveRecord 相比,Castle ActiveRecord具有以下特点:
使用Nhibernate,你繁琐的配置工作多于复杂的映射,而使用ActiveRecord却是推进你的生产力的一个保证,你不必再为编写繁冗复杂的映射文件而头疼,ActiveRecord封装了NHibernate的操作,使用特性来代替映射文件,无论何时你需要,ActiveRecord都能给你一个Isession实例,它提供的简洁的O/R映射会让你为实现持久化数据层是那么简单而惊叹!
官方网址:http://www.castleproject.org/
环境:Windows 8.1+Visual Stidio 2013+Sql Server 2008R2+.Net Framework 4.0
版本:Castle.ActiveRecord-3.0.RC
数据库:ActiveRecord_Blog
数据表:Post
USE [ActiveRecord_Blog] GO /****** Object: Table [dbo].[Post] Script Date: 11/08/2014 11:12:03 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Post]( [Id] [int] IDENTITY(1,1) NOT NULL, [Subject] [nvarchar](64) NULL, [Text] [nvarchar](1024) NULL, [DateAdded] [datetime] NULL, CONSTRAINT [PK_Posts] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO
数据表:Comment
USE [ActiveRecord_Blog] GO /****** Object: Table [dbo].[Comment] Script Date: 11/08/2014 11:11:22 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Comment]( [Id] [int] IDENTITY(1,1) NOT NULL, [Text] [nvarchar](1024) NULL, [Author] [nvarchar](64) NULL, [DateAdded] [datetime] NULL, [PostId] [int] NULL, CONSTRAINT [PK_Comment] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[Comment] WITH CHECK ADD CONSTRAINT [FK_Comment_Post] FOREIGN KEY([PostId]) REFERENCES [dbo].[Post] ([Id]) GO ALTER TABLE [dbo].[Comment] CHECK CONSTRAINT [FK_Comment_Post] GO
如图:
Model-->Castle.ActiveRecord.dll、NHibernate.dll
Web-->Model、Castle.ActiveRecord-3.0.RC中的所有DLL
// 指定数据表 [ActiveRecord("Post")] public class Post : ActiveRecordBase<Post> { // 指定数据表中的主键 [PrimaryKey(PrimaryKeyType.Identity, "Id")] public int Id { get; set; } // 指定数据表中的列 [Property("Subject")] public string Subject { get; set; } [Property("Text")] public string Text { get; set; } [Property("DateAdded")] public DateTime DateAdded { get; set; } //一对多 [HasMany(typeof(Comment), Table = "Comment", ColumnKey = "PostId")] public IList<Comment> Comments { get; set; } // 静态方法,通过主键ID查找 public static Post Find(int id) { return FindByPrimaryKey(id); } }
[ActiveRecord("Comment")] public class Comment : ActiveRecordBase<Comment> { [PrimaryKey("Id")] public int Id { get; set; } [Property("Author")] public string Author { get; set; } [Property("Text")] public string Text { get; set; } [Property("DateAdded")] public DateTime DateAdded { get; set; } [Property("PostId")] public int PostId { get; set; } // 多对一,对应Post的的Comments属性 [BelongsTo(Column = "PostId")] public Post Post { get; set; } }
<?xml version="1.0" encoding="utf-8"?> <activerecord isWeb="true"> <config> <add key="connection.driver_class" value="NHibernate.Driver.SqlClientDriver" /> <add key="dialect" value="NHibernate.Dialect.MsSql2005Dialect" /> <add key="connection.connection_string" value="UID=sa;Password=123456;Initial Catalog=ActiveRecord_Blog;Data Source=." /> <add key="connection.provider" value="NHibernate.Connection.DriverConnectionProvider" /> <add key="proxyfactory.factory_class" value="NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle" /> </config> </activerecord>
void Application_Start(object sender, EventArgs e) { // 在应用程序启动时运行的代码 AuthConfig.RegisterOpenAuth(); RouteConfig.RegisterRoutes(RouteTable.Routes); InitActiveRecord(); } private void InitActiveRecord() { try { string NHibernateFilePath = Server.MapPath("~/NHibernate.config"); XmlConfigurationSource source = new XmlConfigurationSource(NHibernateFilePath); ActiveRecordStarter.Initialize(source, typeof(Model.Post), typeof(Model.Comment)); } catch (Exception) { throw; } }
public void Add() { Model.Post post = new Model.Post() { Subject = "测试2", Text = "测试内容2", DateAdded = DateTime.Now }; post.Create(); }
public void Update() { Model.Post post = new Model.Post(); post = Model.Post.Find(1); post.Subject = "修改后1"; post.Text = "修改后内容1"; post.Update(); }
public void Delete(int id) { Model.Post post = new Model.Post(); post.Id = 1; post.Delete(); Response.Write("删除了ID:" + id + "\n\r"); } public void DeleteAll() { Model.Post.DeleteAll(); }
public void Find(int id) { using (new SessionScope()) { Model.Post post = new Model.Post(); post = Model.Post.Find(id); Response.Write(post.Id.ToString() + "\n\r"); Response.Write(post.Subject + "\n\r"); Response.Write(post.Text + "\n\r"); Response.Write(post.DateAdded.ToString() + "\n\r"); Response.Write(post.Comments.FirstOrDefault().Id); } } public void FindAll() { IList<Model.Post> postList = Model.Post.FindAll(); foreach (var post in postList) { Response.Write(post.Id.ToString() + "\n\r"); Response.Write(post.Subject + "\n\r"); Response.Write(post.Text + "\n\r"); Response.Write(post.DateAdded.ToString() + "\n\r"); } }
源码下载:百度云