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);
    }
  }
}

数据表关系图
uploads/200705/14_111952_ar_rel_1.gif


Group 表
uploads/200705/14_111957_ar_rel_2.gif


User 表
uploads/200705/14_112000_ar_rel_3.gif


如果我们想将 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);
    }
  }
}

数据表关系图
uploads/200705/14_112004_ar_rel_4.gif


User 表
uploads/200705/14_112008_ar_rel_5.gif


Group 表
uploads/200705/14_112011_ar_rel_6.gif


UserGroup 表
uploads/200705/14_112015_ar_rel_7.gif


通过上面的图,我们发现 "ActiveRecordStarter.CreateSchema()" 创建的映射表(UserGroup)并不会创建组合主键,加上我们使用 IList 存储关联对象,因此可能会出现关联对象重复问题。解决的方法是使用自定义类型或者字典来替换 IList。
posted on 2008-01-23 06:52  JerryZhao  阅读(436)  评论(0编辑  收藏  举报