这种映射模式,我们只需要分别对每个类设定各自映射的表就可以了。代码如下面所示:
[TableMap("Parent", "Property1")]
public class Parent
{
private string property1;
private string property2;
[ColumnMap("Column1", DbType.String)]
public string Property1
{
get { return property1; }
set { property1=value; }
}
[ColumnMap("Column2", DbType.String)]
public string Property2
{
get { return property2; }
set { property2 = value; }
}
}
[TableMap("Child1", "Property1")]
public class Child1 : Parent
{
private string property3;
[ColumnMap("Column3", DbType.String)]
public string Property3
{
get { return property3; }
set { property3 = value; }
}
}
[TableMap("Child2", "Property1")]
public class Child2 : Parent
{
private string property4;
[ColumnMap("Column4", DbType.String)]
public string Property4
{
get { return property4; }
set { property4 = value; }
}
} |
此时,当按照如下的代码初始化一个Child1对象,
Child1 c1 = new Child1();
c1.Property1 = "P11";
c1.Property2 = "P12";
c1.Property3 = "P13"; |
并保存到数据库中的时候,数据库中的记录应该是:
Parent表:
Child1表:
同样的,如果保存的是一个Child2对象,那么,将在Parent表和Child2表中添加记录。
5.4设计对象操纵框架
由于我们采用了对象同操作分开的方式,因此,需要设计一个统一的接口来完成对对象的操纵。为了使用的方便性,我们尽量设计少的接口。我们设计以下三个主要接口:
1、 PersistenceManager:这类完成所有对对象的增加、修改以及删除的操作,并提供简单的查询功能。
2、 Query:这个接口完成对对象的查询功能。
3、 Transaction:这个接口负责处理事务。
另外,为了描述对象的状态,定义了EntityState枚举。为了简单化,这里只定义了四个状态,如下面的定义:
public enum EntityState{Transient,New,Persistent,Deleted} |
对上面几个接口的说明分别如下:
? PersistenceManager
这个接口的定义如下:
public enum PersistOptions{SelfOnly,IncludeChildren,IncludeReference,Full}
public interface PersistenceManager : IDisposable
{
void Close();
bool IsClosed{get;}
Transaction CurrentTransaction{ get;}
bool IgnoreCache{get;set;}
void PersistNew(object entity);
void PersistNew(object entity,PersistOptions options);
void Persist(object entity);
void Persist(object entity,PersistOptions options);
void Persist(object entity,params string[] properties);
void Persist(object entity,PersistOptions options,params string[] properties);
void Delete(object entity);
void Delete(object entity,PersistOptions options);
void Attach(object entity);
void Attach(object entity,PersistOptions options);
void Reload(object entity);
void Reload(object entity,PersistOptions options);
void Evict (object entity);
void EvictAll (object[] pcs);
void EvictAll (ICollection pcs);
void EvictAll ();
object FindByPrimaryKey(Type entityType,object id);
object FindByPrimaryKey(Type entityType,object id,PersistOptions options);
T FindByPrimaryKey(object id);
T FindByPrimaryKey(object id, PersistOptions options);
object GetReference(object entity);
object GetReference(object entity,Type[] parents);
object GetChildren(object entity);
object GetChildren(object entity,Type[] children);
EntityState GetState(object entity);
ICollection GetManagedEntities();
bool Flush();
Query NewQuery();
Query NewQuery(Type entityType);
Query NewQuery(Type entityType,string filter);
Query NewQuery(Type entityType,string filter,QueryParameterCollection paramColletion);
Query NewQuery();
Query NewQuery(string filter);
Query NewQuery(string filter, QueryParameterCollection paramColletion);
} |
对于这个接口的几个主要方法说明如下:
PersistNew方法将一个新的实体对象转换成可持续对象,这个对象在事务结束的时候,会被Insert到数据库中。调用这个方法后,该对象的状态为EntityState.New。如果一个对象的状态为EntityState.Persistent,那么,这个方法将抛出一个EntityIsPersistentException异常。
Persist方法将一个实体对象保存到数据库中。如果一个对象是Trasient的,则将其转换为EntityState.New状态。在事务结束的时候,会被Insert到数据库中;否则,其状态就是EntityState.Persist,就更新到数据库中。如果一个Trasient对象实际上已经存在于数据库中,由于Persist方法并不检查实际的数据库,因此,调用这个方法,将会抛出异常。这个时候,应该使用先使用Attach方法,然后调用Persist。Persist方法主要用于已受管的对象的更新。
Delete方法删除一个对象。一个对象被删除后,其状态变成EntityState.Deleted,在事务结束的时候,会被从数据库中删除。 如果一个对象不是持久的,那么,这个方法将抛出异常。
Attach方法将一个对象标记为可持续的。如果这个对象已经存在于实际的数据库中,那么,这个对象的状态就是EntityState.Persistent,否则,这个对象的状态就是EntityState.New。
Reload方法重新从数据库中载入这个对象,这意味着重新给对象的各个属性赋值。
Evict方法从缓存中把某个对象移除。
FindByPrimaryKey方法根据主键查找某个对象,如果主键是多个字段的,主键必须是PrimaryKey数组,否则抛出异常。
? Query:
这个接口的定义如下:
public interface Query
{
Type EntityType{get;set;}
string EntityTypeName{get;set;}
string Filter{get;set;}
QueryParameterCollection Parameters{get;set;}
string Ordering{get;set;}
bool IgnoreCache{get;set;}
PersistOptions Options { get;set;}
ICollection QueryObjects();
DataSet QueryDataSet();
object GetChildren(object entity);
object GetChildren(DataSet dst);
object GetChildren(object entity, Type[] children);
object GetChildren(DataSet entity, Type[] children);
object GetReference(object entity);
object GetReference(DataSet entity);
object GetReference(object entity, Type[] parents);
object GetReference(DataSet entity, Type[] parents);
PersistenceManager PersistenceManager{get;}
bool IsClosed{get;}
void Close ();
void Open();
}
public interface Query : Query
{
new ICollection QueryObjects();
} |
Query接口的主要使用方法,是设定需要查询的对象的类型,以及过滤条件,然后执行QueryObjects方法,就可以得到相应的复合条件的对象。
? Transaction
这个接口主要用于处理事务,提供的功能比较简单,包括事务的开始、提交以及回滚三个主要功能。这个接口的定义如下:
public interface Transaction
{
void Begin();
void Commit();
void Rollback();
PersistenceManager PersistenceManager{get;}
} |
定义好了接口,下面准备实现。这将在下面的小节中描述。
下面的例子展示了一个利用Websharp框架保存一个Order对象的过程:
DatabaseProperty dbp = new DatabaseProperty();
dbp.DatabaseType = DatabaseType.MSSQLServer;
dbp.ConnectionString = "Server=127.0.0.1;UID=sa;PWD=sa;Database=WebsharpTest;";
PersistenceManager pm = PersistenceManagerFactory.Instance().Create(dbp);
Order o = new Order();
o.OrderType = new OrderType(3, "音响");
o.OrderID = 3;
o.Title = "SecondOrder";
o.IsDigned = false;
o.AddTime = DateTime.Now;
o.Details = new List<OrderDetail>(2);
for (int j = 1; j < 3; j++)
{
OrderDetail od= new OrderDetail();
od.OrderID = 3;
od.ProductID = j;
od.Amount = j;
o.Details.Add(od);
}
pm.PersistNew(o, PersistOptions.IncludeChildren);
pm.Flush();
pm.Close(); |
5.5实现对象操纵框架
前面,我们已经定义好了O/R Mapping的基本框架,下面,我们来具体讨论实现这个框架需要的一些主要工作。
在实现中,以下几个方面是比较主要的:
? MetaData
? StateManager
? SqlGenerator
? IEntityOperator
MetaData用来记录对象和数据库之间映射的元数据信息,包括两个部分的元数据:
1、 对象和表映射的信息
2、 对象属性和字段映射的信息。
关于MetaData的定义可以参见Websharp的源代码。
MetaData数据,由专门的类来进行解析,并进行缓存处理。在Websharp中,由MetaDataManager来完成这个任务。MetaDataManager通过反射,读取实体类的信息,来得到MetaData数据。下面的代码片断演示了这个过程的主要内容。
首先,ParseMetaData方法读取类的信息:
private static MetaData ParseMetaData(Type t, DatabaseType dbType)
{
MetaData m = new MetaData();
m.ClassName = t.Name; //类名
m.EntityType = t; //实体类的类型
m.MapTable = GetMappedTableName(t); //实体类映射的表
m.PrimaryKeys = GetKeyColumns(t); //主键
if (m.PrimaryKeys.Length > 1)
m.IsMultiPrimaryKey = true;
else
m.IsMultiPrimaryKey = false;
……
} |
然后,读取每个字段的信息。这个部分的代码比较长,下面,只列出部分代码:
PropertyInfo[] pinfos = t.GetProperties();
m.FieldMetaDatas = new Dictionary<string,FieldMetadata>(pinfos.Length);
foreach (PropertyInfo pinfo in pinfos)
{
FieldMetadata fd = new FieldMetadata();
fd.PropertyName = pinfo.Name;
ColumnMapAttribute cattr = Attribute.GetCustomAttribute(pinfo, typeof(ColumnMapAttribute)) as ColumnMapAttribute;
if(!Object.Equals(null,cattr))
{
fd.ColumnName = cattr.ColumnName;
fd.DbType = cattr.DbType;
fd.DefaultValue = cattr.DefaultValue;
}
else
{
fd.ColumnName = fd.PropertyName ;
fd.DbType = DbType.String;
fd.DefaultValue = String.Empty;
}
……
} |
最后,根据映射信息,构建同数据库进行交互时候的SQL语句。O/R Mapping框架的最后操作,还是回归到根据SQL语句来进行对数据库的操作。
SqlGenerator sg = SqlGenerator.Instance(dbType);
m.SelectSql = sg.GenerateSql(t, OperationType.SelectByKey); |
SQL语句的具体构建,由SqlGenerator来完成。
SqlGenerator是一个抽象类,定义了构建同数据库进行交互的方法接口。在这个抽象类的下面,根据不同的数据库,扩展出针对不同数据库的SQL语句生成器。例如,一个SQL Server的SqlGenerator可以这样来生成插入一条记录需要的SQL语句:
private SqlStruct GenerateInsertSql(Type entityType)
{
string autoP;
bool autoInscrease = MetaDataManager.GetAutoInscreaseProperty(entityType, out autoP);
List<string> lcolumns = MetaDataManager.GetDbColumns(entityType);
string[] parameters = new string[lcolumns.Count];
ParamField[] paramField = new ParamField[lcolumns.Count];
if (autoInscrease)
{
lcolumns.Remove(autoP);
}
string[] columns = lcolumns.ToArray();
for (int i = 0; i < columns.Length; i++)
{
parameters[i] = "@" + columns[i];
paramField[i] = new ParamField(parameters[i], columns[i]);
}
if (autoInscrease)
{
parameters[parameters.Length-1] = "@" + autoP;
paramField[parameters.Length-1] = new ParamField(parameters[parameters.Length - 1], autoP);
}
string tableName = MetaDataManager.GetMappedTableName(entityType);
StringBuilder strSql = new StringBuilder("INSERT INTO ").Append(tableName).Append("(").Append(string.Join(",", columns)).Append(") VALUES(").Append(string.Join(",", parameters)).Append(")");
if (autoInscrease)
{
strSql.Append(";SELECT @").Append(autoP).Append("=@@IDENTITY");
}
return new SqlStruct(strSql.ToString(),paramField);
} |
前面的章节讨论过对象的状态问题。在Websharp中,因为采用了普通的类就可以持久化的操作的方式,因此,需要另外的机制来管理对象的状态。
在Websharp中,为了简化,只定义了四种对象的状态,分别是Transient,New,Persistent和Deleted,定义如下:
public enum EntityState{Transient,New,Persistent,Deleted} |
在实现中,定义了StateManager类来管理对象的状态,这个类的定义如下:
public class StateManager
{
public StateManager(object entity)
{
this.m_Entity = entity;
}
public StateManager(object entity,EntityState state)
{
this.m_Entity = entity;
this.m_State = state;
}
private object m_Entity;
public object Entity
{
get { return m_Entity; }
set { m_Entity = value; }
}
private EntityState m_State;
public EntityState State
{
get { return m_State; }
set { m_State = value; }
}
} |
在PersistenceManager里面,持久化一个对象的时候,如果这个对象不是受管理的,则PersistenceManager会给这个对象分配一个StateManager。例如,当对一个对象执行PersistNew操作的时候,PersistenceManager将首先检查这个对象是否是受管理的,如果不是,则为这个对象分配一个StateManager,并且其状态为EntityState.New,然后,将这个对象添加到待操作列表中,在执行Flush方法的时候,会对这个对象执行一个新增的操作。代码如下:
public void PersistNew(object entity, PersistOptions options)
{
//首先,检查这个对象是否已经是受管理的对象
StateManager smanager;
if (IsManagedBySelf(entity,out smanager))
{
throw new EntityIsPersistentException();
}
//将对象标记为受管理的,并且状态是EntityState.New
smanager = new StateManager(entity,EntityState.New);
stateManagers.Add(smanager);
//添加到操作列表中
opEntityList.Add(new OperationInfo(smanager,options));
} |
最后,在执行Flush方法的时候,PersistenceManager会把所有的对象的变化反应到数据库中。
foreach (OperationInfo opInfo in opEntityList)
{
IEntityOperator op = EntityOperatorFactory.CreateEntityOperator(dao, opInfo.StateManager.State);
op.Execute(opInfo.StateManager.Entity, dao);
CacheProxy.CacheEntity(opInfo.StateManager.Entity);
} |
可以看到,具体的对数据库的操作,通过IEntityOperator接口来完成。IEntityOperator接口定义了执行某个具体的对象同数据库进行交互的接口,在这个接口的下面,扩展出针对各个数据库的具体的实现类。这个部分的结构可以用下面的图来表示:
本文作者:佚名 来源:http://www.foolmen.com/websharp/
CIO之家 www.ciozj.com 微信公众号:imciow