本节主要是实现数据库表一对多之间的映射关系。首先,我们需要创建一个Blogs表和一个Posts表,一个Blogs表中可以有多个文章(Posts),典型的一对多的关系:
Blogs表:
CREATE TABLE [dbo].[Blogs](
[blog_id] [int] IDENTITY(1,1) NOT NULL,
[blog_name] [varchar](50) NULL,
[blog_author] [varchar](50) NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY CLUSTERED
(
[blog_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
Posts表:
CREATE TABLE [dbo].[Posts](
[post_id] [int] IDENTITY(1,1) NOT NULL,
[post_title] [varchar](50) NULL,
[post_contents] [text] NULL,
[post_categories] [varchar](50) NULL,
[post_blogid] [int] NULL,
[post_created] [datetime] NULL,
[post_published] [bit] NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY CLUSTERED
(
[post_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] TEXTIMAGE_ON [PRIMARY]
GO
然后根据前面几章的学习,很轻松的就能够创建Blog和Post操作类:
Blog类如下:
using Castle.ActiveRecord;
using System.Collections;
using NHibernate.Mapping;
namespace CastleLib
{
[ActiveRecord("Blogs")]
public class Blog:ActiveRecordBase
{
private int _id;
private string _name;
private string _author;
private IList _posts;
[PrimaryKey(PrimaryKeyType.Native,"blog_id")]
public int ID
{
get { return _id; }
set { _id = value; }
}
[Property("blog_name")]
public string Name
{
get { return _name; }
set { _name = value; }
}
[Property("blog_author")]
public string Author
{
get { return _author; }
set { _author = value; }
}
//需要注意的是,这里指定了Blogs表和Posts表之间的对应关系,二者之间是通过post_blogid来进行对应的
//并且通过HasMany指定Blogs为主表,可以有多个Posts
[HasMany(typeof(Post),Table="Posts",ColumnKey="post_blogid",Cascade=ManyRelationCascadeEnum.SaveUpdate)]
public IList Posts
{
get { return _posts; }
set { _posts = value; }
}
public static void DeleteAll()
{
DeleteAll(typeof(Blog));
}
public static Blog[] FindAll()
{
return (Blog[])FindAll(typeof(Blog));
}
public static Blog Find(int id)
{
return (Blog)FindByPrimaryKey(typeof(Blog), id);
}
}
}
Post类如下:
using System;
using Castle.ActiveRecord;
namespace CastleLib
{
[ActiveRecord("Posts")]
public class Post:ActiveRecordBase
{
private int _id;
private string _title;
private string _content;
private string _category;
private DateTime _created;
private bool _published;
private Blog _blog;
[PrimaryKey(PrimaryKeyType.Native,"post_id")]
public int ID
{
get { return _id; }
set { _id = value; }
}
[Property("post_title")]
public string Title
{
get { return _title; }
set { _title = value; }
}
[Property(Column="post_contents",ColumnType="StringClob")]
public string Contents
{
get { return _content; }
set { _content = value; }
}
[Property("post_categories")]
public string Category
{
get { return _category; }
set { _category = value; }
}
//注意,这里主要是用来指明Posts和Blogs表之间的对应关系,即Posts表属于Blogs,并且通过post_blogid关联
[BelongsTo("post_blogid")]
public Blog Blog
{
get { return _blog; }
set { _blog = value; }
}
[Property("post_created")]
public DateTime Created
{
get { return _created; }
set { _created = value; }
}
[Property("post_published")]
public bool Published
{
get { return _published; }
set { _published = value; }
}
public static void DeleteAll()
{
DeleteAll(typeof(Post));
}
public static Post[] FindAll()
{
return (Post[])FindAll(typeof(Post));
}
public static Post FindByPrimaryKey(int id)
{
return (Post)FindByPrimaryKey(typeof(Post),id);
}
}
}
下面就来测试,这里我们需要一个Init()函数来初始化Post和Blog类,当然是需要加入到ActiveRecord容器中进行初始化。
下面具体看代码,需要注意的是,在我的测试过程中,出现过两次错误,这两次错误也是很多人在初次使用ActiveRecord的时候容易犯的:
//You have accessed an ActiveRecord class that wasn't properly initialized.
//The only explanation is that the call to ActiveRecordStarter.Initialize() didn't include
出现这个错误是你没有进行初始化。
//Could not perform Save for Post
出现这个的最可能原因是你的数据格式化不对,我这里主要是试图往DateTime类型中插入超出范围的日期导致出错。
具体见代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Castle.ActiveRecord.Framework.Config;
using Castle.ActiveRecord;
using CastleLib;
namespace CastleAPP
{
public partial class OneMany : Form
{
public OneMany()
{
InitializeComponent();
//记住,如果Init()函数没有调用的话,会出现:
//You have accessed an ActiveRecord class that wasn't properly initialized.
//The only explanation is that the call to ActiveRecordStarter.Initialize() didn't include
Init();
}
private void Init()
{
InPlaceConfigurationSource source = new InPlaceConfigurationSource();
IDictionary<string, string> properties = new Dictionary<string, string>();
properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");
properties.Add("dialect", "NHibernate.Dialect.MsSql2008Dialect");
properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
properties.Add("proxyfactory.factory_class", "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
properties.Add("connection.connection_string", "UID=sa;Password=251147;Initial Catalog=CastleBase;Data Source=.;");
source.Add(typeof(ActiveRecordBase), properties);
ActiveRecordStarter.Initialize(source, typeof(Post),typeof(Blog));
}
private void OneMany_Load(object sender, EventArgs e)
{
}
///<summary>
/// 执行事务,持久化对象到数据库
///</summary>
public void TestCascadingSave()
{
//创建Blog对象
Blog blog = new Blog();
blog.Name = "程序诗人的博客空间";
blog.Author = "程序诗人";
//执行事务,持久化对象到数据库
using (TransactionScope tran = new TransactionScope())
{
using (new SessionScope());
try
{
blog.Create();
for (int i = 0; i < 11; i++)
{
Post post = new Post();
post.Title = "这是我的第"+i+"篇文章";
post.Category = "Castle Learn";
post.Created = DateTime.Now; //要给时间赋值,否则会出现Could not perform Save for Post的错误
post.Blog = blog;
post.Save(); //这个地方将文章保存到session中
}
tran.VoteCommit(); //统一提交到数据库中
}
catch
{
tran.VoteRollBack();
}
}
}
///<summary>
/// 级联更新
///</summary>
public void TestCascadingUpdate()
{
Blog blog = Blog.Find(5);
using (TransactionScope tran = new TransactionScope())
{
try
{
for (int i = 0; i < 5; i++)
{
Post post = new Post();
post.Title = "哈哈哈,这是第"+i+"次写文章了。";
post.Category = "杂记";
post.Created = DateTime.Now;
post.Save();
//blog.Posts.Add(post);
//准确的来讲,这两句话作用相同
post.Blog = blog;
}
blog.Update();
tran.VoteCommit();
}
catch
{
tran.VoteRollBack();
}
}
}
private void button1_Click(object sender, EventArgs e)
{
TestCascadingSave();
}
private void button2_Click(object sender, EventArgs e)
{
TestCascadingUpdate();
}
}
}
希望有用,谢谢。