使用主动实体(ActiveEntity)简化继承体系映射类的写操作
在上一篇文章ORM中的继承关系映射全解中,演示了各种继承关系映射在NBear中实现示例,只不过,文中的示例对于一实体一具体表和一实体一扩展表方案下的子类的保存操作,需要用户手动save实体本身和其所有的父类对应的表。这多少有点不爽,因为,实体的用户是很难全都了解具体要save多少表的。为了解决这个问题,在最新的v2.1.6版中,为NBear新增了一个ActiveEntity类。ActiveEntity类最大的作用是封装了子类的插入,修改和删除操作。另外,ActiveEntity能够自动记录修改过的字段属性,因此,写数据库时,它只会Update真正修改过的实体的属性。在下面的示例中我们可以看到,如何使用ActiveEntity来简化前文介绍的实体继承中的一实体一具体表和一实体一扩展表示例。更多细节可以参考NBear中文用户手册。
--
NBear v2.1.6 除了新增了本文探讨的ActiveEntity类,还有如下重要更新:
1)Gateway.ExecuteProcedureXXX方法新增了对存储过程输出参数的支持;
2)修复了一个Gateway.Save中可能导致事务死锁的bug;
--
ActiveEntity包含如下成员:
范型参数列表
//Must give an IEntity when creating an instance
public class ActiveEntity<IEntityType> where IEntityType : IEntity
构造函数
//Create an active entity based on an given entity
public ActiveEntity(Gateway gateway, IEntityType obj)
//Create an active entity by given values of PKs, if no values are given, create an empty new active entity.
public ActiveEntity(Gateway gateway, params object[] pkValues)
属性
//The status of active entity, available values are ActiveEntityStatus.New and ActiveEntityStatus.Loaded.
public ActiveEntityStatus Status
//The entity can be read/write directly
public IEntityType Entity
公共方法
public void Save()
public void Save(DbTransaction tran)
public void Delete()
public void Delete(DbTransaction tran)
使用示例
1. 简单示例
对于一个简单的Northwind中的Category实体,我们可以这样简化对其的Save和Delete操作:
[Table("Categories")]
public interface Category : IEntity
{
[PrimaryKey]
int CategoryID { get; }
string CategoryName { get; set; }
string Description { get; set; }
System.Byte[] Picture { get; set; }
}
--
ActiveEntity<Category> obj = new ActiveEntity<Category>(gateway, 1); //Load
Assert.AreEqual(obj.Status, ActiveEntityStatus.Loaded); //After Load, The status become Loaded
string newCatName = Guid.NewGuid().ToString().Substring(0, 5);
obj.Entity.CategoryName = newCatName;
obj.Save(); //do saving, since only CategoryName is modified, the sql created and executed behind the framework only updates the CategoryName column of Categories table
ActiveEntity<Category> obj2 = new ActiveEntity<Category>(gateway); //create a new empty active entity for Category
Assert.AreEqual(obj2.Status, ActiveEntityStatus.New); //A new active entity's status is New before saving
obj2.Entity.CategoryName = obj.Entity.CategoryName + "_new";
obj2.Entity.Description = obj.Entity.Description;
obj2.Entity.Picture = obj.Entity.Picture;
obj2.Save(); //do inserting
obj2.Delete(); //do deleting
2. 简化一实体一具体表映射中的保存
实体定义还是和一实体一具体表中的示例一样的代码,但是新的保存代码却大大简洁了:
[Table("ParentTable")]
public interface Parent : IEntity
{
[PrimaryKey]
int ID { get; }
string Name { get; set; }
}
[Table("AnotherParentTable")]
public interface AnotherParent : IEntity
{
[PrimaryKey]
int ID { get; }
int Age { get; set; }
}
[Table("ChildTable")]
public interface Child : Parent, AnotherParent
{
[PrimaryKey]
new int ID { get; set; }
DateTime Birthday { get; set; }
}
--
//obj is a Child instance
ActiveEntity<Child> activeObj = new ActiveEntity<Child>(gateway, obj);
activeObj.Save();
我们可以看到,当执行ActiveEntity<Child>.Save()时,它会自动执行等价于如下三条代码逻辑:
Gateway.Save<Child>(obj, tran);
Gateway.Save<Parent>(obj, tran);
Gateway.Save<AnotherParent>(obj, tran);
当然,即使实体有更复杂的继承关系,ActiveEntity也能自动保持一致的更新,这样就真正做到了对继承体系的更新和删除对用户是完全透明的。