http://www.rainsts.net/article.asp?id=484
Castle ActiveRecord 对于数据实体的设计非常灵活,大量特性的使用,使得其代码方式非常类似 WCF 的声明式编程。

1. 实体类型

uploads/200705/08_124149_ar2.gif


通常我们会选择从 ActiveRecordBase (或其泛型版本) 继承实体类型,它几乎提供了实体所需的所有操作方法。
[Serializable]
public abstract class ActiveRecordBase<T> : ActiveRecordBase
{
  protected internal static int CountAll();
  protected internal static void Create(T instance);
  protected internal static void Delete(T instance);
  public static void DeleteAll();
  protected static object Execute(NHibernateDelegate call, object instance);
  public static bool Exists();
  public static T Find(object id);
  public static T[] FindAll();
  public static T[] FindAllByProperty(string property, object value);
  protected internal static T FindByPrimaryKey(object id);
  public static T FindFirst(params ICriterion[] criterias);
  public static T FindOne(params ICriterion[] criterias);
  protected internal static void Refresh(T instance);
  protected internal static void Save(T instance);
  public static T[] SlicedFindAll(int firstResult, int maxResults...);
  public static T TryFind(object id);
  protected internal static void Update(T instance);
  
  ...
}

我们看一个简单的例子。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private int id;

  [PrimaryKey(Generator=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; }
  }
}

public class ARTester
{
  public static void Test()
  {
    ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(),
      new XmlConfigurationSource("ar.xml"));

    ActiveRecordStarter.DropSchema();
    ActiveRecordStarter.CreateSchema();

    User user = new User();
    user.Name = "tom";
    user.Save();

    User user2 = User.Find(user.Id);
    Console.WriteLine(user2.Name);
    user2.Name = "tomxxx";
    user2.Update();

    user.Refresh();
    Console.WriteLine(user.Name);
  }
}

从 ActiveRecordBase 继承并不是强制的,我们可以使用 ActiveRecordMediator 来达到同样的目的。
[ActiveRecord("Users")]
public class User
{
  private int id;

  [PrimaryKey(Generator=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; }
  }
}

public class ARTester
{
  public static void Test()
  {
    ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(),
      new XmlConfigurationSource("ar.xml"));

    ActiveRecordStarter.DropSchema();
    ActiveRecordStarter.CreateSchema();

    User user = new User();
    user.Name = "tom";
    ActiveRecordMediator<User>.Save(user);

    User user2 = ActiveRecordMediator<User>.FindByPrimaryKey(user.Id);
    Console.WriteLine(user2.Name);
    user2.Name = "tomxxx";
    ActiveRecordMediator<User>.Update(user2);

    ActiveRecordMediator<User>.Refresh(user);
    Console.WriteLine(user.Name);
  }
}

除了 ActiveRecordBase,AR 还提供了ActiveRecordValidationBase。它允许我们使用相应的特性对实体成员进行格式验证,其具体使用方法我们会在下一章研究。

2. 映射特性

AR 提供了大量的特性来代替 NHibernate 配置文件,下图是我们常用的实体映射特性。

uploads/200705/08_124153_ar3.gif


ActiveRecordAttribute

主要用于指定实体类型和数据表之间的映射关系,我们可以通过构造参数或者 Table 属性设置数据表名称。它还拥有其他大量的属性用来操控实体的缓存策略、继承策略等,具体信息可参考帮助文件。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
}

PrimaryKeyAttribute

用于指定数据表的主键,包括自增字段等。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private int id;

  [PrimaryKey(PrimaryKeyType.Identity)]
  public int Id
  {
    get { return id; }
    set { id = value; }
  }
}

我们也可以直接使用字符串类型的字段作为主键。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private string name;

  [PrimaryKey(PrimaryKeyType.Assigned)]
  public string Name
  {
    get { return name; }
    set { name = value; }
  }
}

不过,这似乎并不是一个好主意,我个人建议还是使用一个标识字段要好一些。当然,我们可以不使用自增类型。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private string id;

  [PrimaryKey(PrimaryKeyType.UuidHex)]
  public string Id
  {
    get { return id; }
    set { id = value; }
  }

  private string name;

  [Property(Unique=true, NotNull=true)]
  public string Name
  {
    get { return name; }
    set { name = value; }
  }
}

PropertyAttribute / FieldAttribute

这两个特性用来指定实体类型属性、字段与数据表字段的映射关系。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private string id;

  [PrimaryKey(PrimaryKeyType.UuidHex)]
  public string 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 int age;

  [Property(Column="Age1")]
  public int Age
  {
    get { return age; }
    set { age = value; }
  }
}

FieldAttribute 的使用方式基本类似。成员属性还包括指定是否不允许为空(NotNull)、约束(Check)、计算公式(Formula)等。最有意思的恐怕就是 Insert & Update 了,它允许我们在执行插入和更新动作时忽略该属性或字段。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private string id;

  [PrimaryKey(PrimaryKeyType.UuidHex)]
  public string 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 DateTime createDateTime;

  [Property(Update=false)]
  public DateTime CreateDateTime
  {
    get { return createDateTime; }
    set { createDateTime = value; }
  }
}

public class ARTester
{
  public static void Test()
  {
    ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(),
      new XmlConfigurationSource("ar.xml"));

    ActiveRecordStarter.DropSchema();
    ActiveRecordStarter.CreateSchema();

    User user = new User();
    user.Name = "tom";
    user.CreateDateTime = DateTime.Now;
    user.Save();

    Console.WriteLine(user.CreateDateTime);
    System.Threading.Thread.Sleep(2000);

    User user2 = User.Find(user.Id);
    user2.Name = "abc";
    user2.CreateDateTime = DateTime.Now.AddDays(100);
    user2.Update();

    user.Refresh();
    Console.WriteLine(user.Name);
    Console.WriteLine(user.CreateDateTime);
  }
}

输出:
2007-5-8 12:12:46
abc
2007-5-8 12:12:46

这东东还是非常实用的。

CompositeKeyAttribute / KeyPropertyAttribute

用于定义数据表中的组合键。注意,组合键类型必须是可序列化,且重写了 Equals 和 GetHashCode 方法。
[Serializable]
public class Id
{
  private int x;

  [KeyProperty]
  public int X
  {
    get { return x; }
    set { x = value; }
  }

  private int y;

  [KeyProperty]
  public int Y
  {
    get { return y; }
    set { y = value; }
  }

  public override bool Equals(object obj)
  {
    return base.Equals(obj);
  }

  public override int GetHashCode()
  {
    return base.GetHashCode();
  }
}

[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private Id id;

  [CompositeKey]
  public Id Id
  {
    get { return id; }
    set { id = value; }
  }

  private string name;

  [Property(Unique = true, NotNull = true)]
  public string Name
  {
    get { return name; }
    set { name = value; }
  }
}

public class ARTester
{
  public static void Test()
  {
    ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(),
      new XmlConfigurationSource("ar.xml"));

    ActiveRecordStarter.DropSchema();
    ActiveRecordStarter.CreateSchema();

    User user = new User();
    user.Id = new Id();
    user.Id.X = 1;
    user.Id.Y = 2;
    user.Name = "zs";
    user.Create(); // 调用 Save() 会出错。????
  }
}

uploads/200705/08_124158_ar4.gif


NestedAttribute

NestedAttribute 用来标注自定义类型的属性。
public class PostalAddress
{
  private string country;

  [Property]
  public string Country
  {
    get { return country; }
    set { country = value; }
  }

  private string address;

  [Property]
  public string Address
  {
    get { return address; }
    set { address = value; }
  }

  private string postcode;

  [Property]
  public string Postcode
  {
    get { return postcode; }
    set { postcode = 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 PostalAddress address;

  [Nested]
  public PostalAddress Address
  {
    get { return address; }
    set { address = value; }
  }
}

public class ARTester
{
  public static void Test()
  {
    ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(),
      new XmlConfigurationSource("ar.xml"));

    ActiveRecordStarter.DropSchema();
    ActiveRecordStarter.CreateSchema();

    User user = new User();
    user.Name = "zhagnsan";
    user.Address = new PostalAddress();
    user.Address.Country = "China";
    user.Address.Address = "Beijing...";
    user.Address.Postcode = "123456";

    user.Save();

    User user2 = User.Find(user.Id);
    Console.WriteLine(user2.Address.Address);
  }
}

uploads/200705/08_124202_ar5.gif


------------------------ 注意 --------------------------------

1. 如果某个属性必须是只读的,那么我们就必须指定其 Access。
[ActiveRecord("Users")]
public class User : ActiveRecordBase<User>
{
  private int id;

  [PrimaryKey(PrimaryKeyType.Identity, Access=PropertyAccess.FieldCamelcase)]
  public int Id
  {
    get { return id; }
  }
}

2. 如果需要使用大尺寸数据,比如长度超过 255 的字符串,或者 byte[],那么我们最好使用 "StringClob" 或者 "BinaryBlob"。
[ActiveRecord]
public class Data : ActiveRecordBase<Data>
{
  private int id;

  [PrimaryKey(PrimaryKeyType.Identity, Access=PropertyAccess.FieldCamelcase)]
  public int Id
  {
    get { return id; }
  }

  private string content;

  [Property(ColumnType = "StringClob", Length = 2048)]
  public string Content
  {
    get { return content; }
    set { content = value; }
  }
}
[最后修改由 yuhen, 于 2007-05-08 15:39:03]
posted on 2008-01-23 06:12  JerryZhao  阅读(749)  评论(0编辑  收藏  举报