搜索 | 会员  
设计一个O/RMapping框架(3)
来源: http://www.foolmen.com/websharp/   作者:佚名  日期:2007/10/30  类别:编程语言  主题:.NET  编辑:dezai
>这种映射模式,我们只需要分别对每个类设定各自映射的表就可以了。代码如下面所示:

这种映射模式,我们只需要分别对每个类设定各自映射的表就可以了。代码如下面所示:

    [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表:

Column1

Column2

P11

P12

Child1表:

Column1

Column3

P11

P13

 

同样的,如果保存的是一个Child2对象,那么,将在Parent表和Child2表中添加记录。

 

    54设计对象操纵框架  

由于我们采用了对象同操作分开的方式,因此,需要设计一个统一的接口来完成对对象的操纵。为了使用的方便性,我们尽量设计少的接口。我们设计以下三个主要接口:

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方法,然后调用PersistPersist方法主要用于已受管的对象的更新。

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

 

55实现对象操纵框架  

前面,我们已经定义好了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 ServerSqlGenerator可以这样来生成插入一条记录需要的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中,为了简化,只定义了四种对象的状态,分别是TransientNewPersistentDeleted,定义如下:

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接口定义了执行某个具体的对象同数据库进行交互的接口,在这个接口的下面,扩展出针对各个数据库的具体的实现类。这个部分的结构可以用下面的图来表示:

德仔网尊重行业规范,每篇文章都注明有明确的作者和来源;德仔网的原创文章,请转载时务必注明文章作者和来源:德仔网;
头条那些事
大家在关注
广告那些事
我们的推荐
也许感兴趣的
干货