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