http://www.rainsts.net/article.asp?id=487
Castle ActiveRecord 通过特性处理数据表关联关系。
- Many-to-one: BelongsToAttribute
- One-to-many: HasManyAttribute
- Many-to-many: HasAndBelongsToManyAttribute
1. One-to-Many / Many-to-One
多数时候,Many-to-One 和 One-To-Many 总算相伴而生的。在下面的例子中,每个组可以有多个用户加入。我们通过在 User 类型里面使用 BelongsTo 添加一个名为 "GroupId" 的字段来处理 Many-to-One 的关联,而在 Group 中我们使用 HasMany 处理 One-to-Many。注意 HasMany 的参数,分别是 "User 类型对象","BelongsTo 所定义的字段名",以及 "User 的表名"。当然,我们可以直接缩写成 HasMany(typeof(User), "GroupId", "Users") 。
[ActiveRecord("Groups")]
public class Group : ActiveRecordBase<Group>
{
private int id;
[PrimaryKey(PrimaryKeyType.Identity)]
public int Id
{
get { return id; }
set { id = value; }
}
private string name;
[Property(Unique = true, NotNull = true)]
public string Name
{
get { return name; }
set { name = value; }
}
private IList users = new List<User>();
[HasMany(typeof(User), ColumnKey = "GroupId", Table = "Users")]
public IList Users
{
get { return users; }
set { users = value; }
}
}
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
private int id;
[PrimaryKey(PrimaryKeyType.Identity)]
public int Id
{
get { return id; }
set { id = value; }
}
private string name;
[Property(Unique = true, NotNull = true)]
public string Name
{
get { return name; }
set { name = value; }
}
private Group group;
[BelongsTo("GroupId")]
public Group Group
{
get { return group; }
set { group = value; }
}
}
public class ARTester
{
public static void Test()
{
ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(), new XmlConfigurationSource("ar.xml"));
ActiveRecordStarter.DropSchema();
ActiveRecordStarter.CreateSchema();
Group group = new Group();
group.Name = "Group1";
group.Save();
for (int i = 0; i < 10; i++)
{
User user = new User();
user.Name = "User" + i;
user.Group = group;
user.Save();
}
// ---------------
foreach (User _user in Group.FindAllByProperty("Name", "Group1")[0].Users)
{
Console.WriteLine("{0},{1}", _user.Name, _user.Group.Name);
}
}
}
数据表关系图
Group 表
User 表
如果我们想将 HasMany 改成强类型,需要额外下载
NHibernate Generics,详细代码可参考 "
Generics support"。
2. Many-to-Many
Many-to-Many 是通过额外的映射表来建立关联关系的。注意 HasAndBelongsToMany 的参数,分别是 "关联目标类型"、"映射表名"、"本类型在映射表中主键字段名(ColumnKey)"、"关联类型在映射表中主键字段名(ColumnRef)"。两个关联类型的相关参数必须一致。
我们修改一下上面的例子,让用户可以同时加入多个组,这样就实现了 Many-to-Many 的需求。
[ActiveRecord("Groups")]
public class Group : ActiveRecordBase<Group>
{
private int id;
[PrimaryKey(PrimaryKeyType.Identity)]
public int Id
{
get { return id; }
set { id = value; }
}
private string name;
[Property(Unique = true, NotNull = true)]
public string Name
{
get { return name; }
set { name = value; }
}
private IList users = new List<User>();
[HasAndBelongsToMany(typeof(User), Table = "UserGroup", ColumnKey = "GroupId", ColumnRef = "UserId")]
public IList Users
{
get { return users; }
set { users = value; }
}
}
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
private int id;
[PrimaryKey(PrimaryKeyType.Identity)]
public int Id
{
get { return id; }
set { id = value; }
}
private string name;
[Property(Unique = true, NotNull = true)]
public string Name
{
get { return name; }
set { name = value; }
}
private IList groups;
[HasAndBelongsToMany(typeof(Group), Table = "UserGroup", ColumnKey = "UserId", ColumnRef = "GroupId")]
public IList Groups
{
get { return groups; }
set { groups = value; }
}
}
public class ARTester
{
public static void Test()
{
ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(), new XmlConfigurationSource("ar.xml"));
ActiveRecordStarter.DropSchema();
ActiveRecordStarter.CreateSchema();
for (int i = 0; i < 10; i++)
{
User user = new User();
user.Name = "User" + i;
user.Save();
}
for (int i = 0; i < 10; i++)
{
Group group = new Group();
group.Name = "Group" + i;
group.Save();
}
User user1 = User.FindAllByProperty("Name", "User1")[0];
user1.Groups.Add(Group.FindAllByProperty("Name", "Group1")[0]);
user1.Groups.Add(Group.FindAllByProperty("Name", "Group2")[0]);
user1.Groups.Add(Group.FindAllByProperty("Name", "Group3")[0]);
user1.Save();
Group group1 = Group.FindAllByProperty("Name", "Group1")[0];
group1.Users.Add(User.FindAllByProperty("Name", "User7")[0]);
group1.Users.Add(User.FindAllByProperty("Name", "User8")[0]);
group1.Save();
// ------------
foreach (Group g in User.FindAllByProperty("Name", "User1")[0].Groups)
{
Console.WriteLine(g.Name);
}
foreach (User u in Group.FindAllByProperty("Name", "Group1")[0].Users)
{
Console.WriteLine(u.Name);
}
}
}
数据表关系图
User 表
Group 表
UserGroup 表
通过上面的图,我们发现 "ActiveRecordStarter.CreateSchema()" 创建的映射表(UserGroup)并不会创建组合主键,加上我们使用 IList 存储关联对象,因此可能会出现关联对象重复问题。解决的方法是使用自定义类型或者字典来替换 IList。