อีกครั้งหนึ่งกับ Prevalence Facility
ต่อเนื่องจากบล็อกตอนที่แล้ว คราวนี้เราจะลองใช้ Bamboo.Prevalence ดูบ้าง เพราะตอนที่แล้ว เราแค่จัดการให้ SnapshotTaker ทำ snapshot ของออปเจคทุก 10 นาที และก่อนออกจากโปรแกรม (อย่างหลังเป็นของ PrevalenceFacility)
คราวนี้เราจะสร้างคลาส PrevalenceUserDataAccess ขึ้นมาหุ้มคลาส MemoryUserDataAccess ไว้อีกที
โดยความเปลี่ยนแปลงในระบบ Prevalence จะต้องทำผ่าน Command Object (ICommand) การ Query ก็จะทำงานผ่าน Query Object เช่นกัน และ Command Object จะต้อง Serializable ด้วย
ทุกคำสั่งที่ทำผ่าน Command Object จะถูกเก็บลง commandlog ทุกครั้ง เมื่อเปิดโปรแกรมขึ้นมา ตัว PrevalenceEngine ก็จะทำการอ่านล็อกไฟล์เพื่อดึง persistent state กลับมา
สำหรับ model เราจะใช้ User ตัวเดียวกับบล็อกที่แล้ว
public class PrevalenceUserDataAccess : IUserDataAccess
{
private PrevalenceEngine _engine;
public PrevalenceUserDataAccess()
{
}
public PrevalenceUserDataAccess(PrevalenceEngine engine) : this()
{
this._engine = engine;
}
public int Append(User user)
{
return (int)this._engine.ExecuteCommand(new AddUserCommand(user));
}
public User FindByUserName(string userName)
{
return (User)this._engine.ExecuteQuery(new UserNameQuery(userName));
}
public User[] FindAll()
{
return (User[])this._engine.ExecuteQuery(new AllUsersQuery());
}
}
การทำงานต้องทำผ่าน Command Object ด้านล่างคือที่โค้ดเพิ่ม User
[Serializable]
public class AddUserCommand : ICommand
{
private User _user;
public AddUserCommand(User user)
{
if (user == null) {
throw new ArgumentNullException();
}
_user = user;
}
public object Execute(object system)
{
IUserDataAccess dao = system as IUserDataAccess;
return dao.Append(this._user);
}
}
Command Object ต้อง Serializable เพราะ Bamboo.Prevalence จะทำการ serialize Command Object ลงดิสก์ทุกครั้ง
และ Query Object
public class AllUsersQuery : IQuery
{
public object Execute(object system)
{
IUserDataAccess dao = system as IUserDataAccess;
return dao.FindAll();
}
}
public class UserNameQuery : IQuery
{
private string _username;
public UserNameQuery(string username)
{
this._username = username;
}
public object Execute(object system)
{
IUserDataAccess dao = system as IUserDataAccess;
return dao.FindByUserName(_username);
}
}
และสุดท้ายคือ App.Config สำหรับร้อย(wire)ออปเจคเข้าด้วยกันครับ
<castle>
<components>
<!-- GUI Components -->
<component
id="form.main"
type="UserTracking.GUI.FrmAddNewUser, UserTracking"
lifecycle="transient" />
<!-- DAO Components -->
<component
id="dao.user"
service="UserTracking.DataAccess.IUserDataAccess, UserTracking"
type="UserTracking.DataAccess.PrevalenceUserDataAccess, UserTracking" />
</components>
<facilities>
<facility
id="prevalence"
type="Castle.Facilities.Prevalence.PrevalenceFacility, Castle.Facilities.Prevalence">
<engines>
<engine
id="prevalenceengine"
autoVersionMigration="true"
storageDir="storage"
snapshotIntervalInHours="0.16"
systemId="userModel"
systemType="UserTracking.DataAccess.MemoryUserDataAccess, UserTracking" />
</engines>
</facility>
</facilities>
</castle>
แต่ถ้าต้องการแค่ persist สถานะของออปเจ็คอาจจะใช้ TransparentEngine แทน โดยใช้ TransparentPrevalenceAttribute ซึ่งต้อง subclass MarshalByRefObject
[Serializable]
public class PrevalenceUserDataAccess : MarshalByRefObject, IUserDataAccess
{
...
Query Object ก็ใช้ QueryAttribute
[Query]
public User FindByUserName(string userName)
{
....