[Castle ActiveRecord] 2. ActiveRecord

Castle ActiveRecord 對於數據實體的設計非常靈活,大量特性的使用,使得其代碼方式非常類似 WCF 的聲明式編程。

1. 實體類型

通常我們會選擇從 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 配置文件,下圖是我們常用的實體映射特性。


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() 會出錯。????
    }
}


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


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

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章