Websharp使用说明
Websharp使用说明
一. 前言
Websharp的目标,是开发一个开源的基于Microsoft.Net的轻量级的应用软件系统开发框架,包含以下内容:
Ø 一个轻量级的O/R Mapping框架
Ø 一个轻量级的AOP框架
Ø 一个轻量级的ServiceLocator,主要目的是为整合不同服务端技术的客户端编程。
说来惭愧的是,这个框架从三年前就开始做了,但是因为工作的原因,具体的开发过程一直是断断续续,中间因个人对编程思想认识的变化,在结构方面也一直有点变化。在这个过程中,一直有人来Mail询问相关的情况,也有很多朋友发信来鼓励我,使我感到愧疚的是,我一直都没有很好的回复这些邮件。在国内,做开源项目真的是很难,首先要养活自己和老婆孩子,才能够挤出一点点时间来做自己的事情。
最近,因为工作的变化,使我从繁复的开发工作中脱离出来,使我有更多的时间来支配我自己,因此,又把这个工作继续了下去,经过一段时间的“虾米”生活,终于把O/R Mapping做的比较像样了。AOP和ServiceLocator部分,是以前做好的,一直都没有动过。现在我把所有的代码都公开出来,并写了一个简单的使用说明,希望能够对大家有所帮助。在这里,需要感谢的是徐芳波,在Websharp的开发过程中,也做了很多的工作,付出了很多。
本文主要说明Websharp O/R Mapping部分使用的内容。关于Websharp的设计理念,以及其他方面的内容,可以访问我的Blog:http://sunnyym.cnblogs.com/
二. Websharp O/R Mapping起步
Websharp ORM是一个轻量级的O/R Mapping框架,主要特点是抛弃了Java中常见的,也是其他一些O/R Mapping产品中使用XML文件来做映射描述的方法,而使用Attribute作为描述映射的方法,简单明了,并且,对开发人员来说,只有PersistenceManager、Query、Transaction等极少数接口需要掌握,上手快,使用非常方便。
因为在某些地方使用了范型,所以目前版本的Websharp需要.Net Framework2.0。
下面的例子给出了使用Websharp进行ORM操作的基本过程。
使用Visual Studio.Net2005建立一个Windows Console项目,然后,定义如下一个类:
[TableMap("Product", "ID")] public class Product { private int m_ID; private string m_Name; private decimal m_Price; public Product() { } public Product(string name,decimal price) { //m_ID = id; m_Name = name; m_Price = price; } [ColumnMap("ID", DbType.Int32)] [AutoIncrease(1)] public int ID { get { return m_ID; } set { m_ID = value; } } [ColumnMap("Name", DbType.String)] public string Name { get { return m_Name; } set { m_Name = value; } } [ColumnMap("Price", DbType.Decimal)] public decimal Price { get { return m_Price; } set { m_Price = value; } } } |
类中第一行[TableMap("Product", "ID")]的意思是说,这个Product类映射到数据库中的Product表,主键为属性ID。在属性ID上,[ColumnMap("ID", DbType.Int32)]的意思是说,这个属性映射到数据库中的ID字段,数据类型是Int32,[AutoIncrease(1)]表明这是一个自动增长列,增长的幅度为1。其他属性的可以据此类推。
因此,上面这个Produc类对应的数据库表的结构应该是:
字段名 |
数据类型 |
ID |
int(自动增长) |
Name |
Nvarchar(50) |
Price |
Decimal |
下面的代码演示了Websharp是如何把Product对象新增到数据库中的。
public static void { //设定环境 DatabaseProperty dbp = new DatabaseProperty(); dbp.DatabaseType = DatabaseType.MSSQLServer; dbp.ConnectionString = "Data Source=(local);Initial Catalog=WebsharpTest;Integrated Security=True"; //操作数据 PersistenceManager pm = PersistenceManagerFactory.Instance().Create(dbp); Product p0 = new Product("Name0", 100); pm.PersistNew(p0); Product p1 = new Product("Name1", 101); pm.PersistNew(p1); pm.Flush(); pm.Close(); } |
在代码的开始部分,先设定了数据库的一些参数,然后,通过PersistenceManagerFactory创建了一个PersistenceManager,用这个PersistenceManager就可以对对象进行操作了。在上面的代码里面,我们把两个Product对象保存到了数据库中。注意,因为Product类的ID属性是自动增长的,因此,在保存这两个对象之前,是不必对ID属性赋值的,在这两个对象被保存到数据库中后,PersistenceManager会根据数据库中的自动增长的值自动给ID赋值。
当然,为了使上面的代码能够顺利运行,我们还需要做一些配置。新建一个App.config文件,把下面的配置代码Copy进去就可以了:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version= <section name="WebsharpExpirationPolicy" type="Websharp.ORM.Service.WebsharpCofigurationHandler,Websharp.ORM.Service" /> </configSections> <cachingConfiguration defaultCacheManager="Cache Manager"> <cacheManagers> <add expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="Null Storage" name="Cache Manager" /> <add expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="Null Storage" name="EntityCache" /> <add expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="Null Storage" name="SqlCache" /> </cacheManagers> <backingStores> <add encryptionProviderName="" type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version= name="Null Storage" /> </backingStores> </cachingConfiguration> <WebsharpExpirationPolicy> <ExpirationPolicy ExpirationCheckInterval="60" AssemblyName="Microsoft.ApplicationBlocks.Cache" ClassName="Microsoft.ApplicationBlocks.Cache.ExpirationsImplementations.SlidingTime" /> </WebsharpExpirationPolicy> </configuration> |
配置文件只是用来说明缓存的使用的,基本上所有的项目需要的配置文件对于Websharp来说都不需要改变。当然,有一些细微的参数还是可以调整的,这个在后面描述。
可以看出,相对于其他一些O/R Mapping框架来说,Websharp不需要写一大堆的XML映射文件,代码简洁明了。
三. 映射方法说明
映射部分,完成对象和关系型数据库之间映射关系的表达。Websharp使用Attribute来描述映射关系,设计了以下Attribute来描述对象和关系型数据库之间的映射。
Ø TableMapAttribute
这个Attribute描述对象和数据库表的映射关系,这个类有两个属性,TableName属性指明和某个类所对应的数据库表,PrimaryKeys用来描述表的主关键字。这个类的定义如下:
[AttributeUsage(AttributeTargets.Class)] public class TableMapAttribute : Attribute { private string tableName; private string[] primaryKeys; public TableMapAttribute(string tableName,params string[] primaryKeys) { this.tableName = tableName; this.primaryKeys = primaryKeys; } public string TableName { get{return tableName;} set{tableName = value;} } public string[] PrimaryKeys { get{return primaryKeys;} set{primaryKeys = value;} } } |
Ø ColumnMapAttribute
这个Attribute描述对象属性和数据库中表的字段之间的映射关系,这个类有三个属性,ColumnName属性指明和某个属性所对应的字段,DbType属性指明数据库字段的数据类型,DefaultValue指明字段的默认值。这个类的定义如下:
[AttributeUsage(AttributeTargets.Property)] public class ColumnMapAttribute : Attribute { private string columnName; private DbType dbtype; private object defaultValue; public ColumnMapAttribute(string columnName,DbType dbtype) { this.columnName = columnName; this.dbtype = dbtype; } public ColumnMapAttribute(string columnName,DbType dbtype,object defaultValue) { this.columnName = columnName; this.dbtype = dbtype; this.defaultValue = defaultValue; } public string ColumnName { get{return columnName;} set{columnName = value;} } public DbType DbType { get{return dbtype;} set{dbtype = value;} } public object DefaultValue { get{return defaultValue;} set{defaultValue = value;} } } |
Ø ReferenceObjectAttribute
ReferenceObjectAttribute指示该属性是引用的另外一个对象,因此,在执行持久化操作的时候,需要根据参数进行额外的处理。默认情况下,当持久化实体对象的时候,ReferenceObjectAttribute指示的属性,不进行操作。这个类有三个属性,ReferenceType指明所引用的对象的类型,PrimaryKey和ForeignKey用来指明两个类之间进行关联的主键和外键。这个类的定义如下:
[AttributeUsage(AttributeTargets.Property)] public class ReferenceObjectAttribute : Attribute { private Type referenceType; private string primaryKey; private string foreignKey; public ReferenceObjectAttribute(Type referenceType,string primaryKey,string foreignKey) { this.referenceType = referenceType; this.primaryKey = primaryKey; this.foreignKey = foreignKey; } public ReferenceObjectAttribute(){} public Type ReferenceType { get{return referenceType;} set{referenceType = value;} } public string PrimaryKey { get{return primaryKey;} set{primaryKey = value;} } public string ForeignKey { get{return foreignKey;} set{foreignKey = value;} } } |
Ø SubObjectAttribute
SubObjectAttribute指示该属性是引用的是子对象,因此,在执行持久化操作的时候,需要根据参数进行额外的处理。默认情况下,当持久化实体对象的时候,SubObjectAttribute指示的属性,不进行操作。
这个类的定义如下:
[AttributeUsage(AttributeTargets.Property)] public class SubObjectAttribute : Attribute { private Type subObjectType; private string primaryKey; private string foreignKey; public SubObjectAttribute(Type subObjectType, string primaryKey, string foreignKey) { this.subObjectType = subObjectType; this.primaryKey = primaryKey; this.foreignKey = foreignKey; } public SubObjectAttribute(){} public Type SubObjectType { get { return subObjectType; } set { subObjectType = value; } } public string PrimaryKey { get{return primaryKey;} set{primaryKey = value;} } public string ForeignKey { get{return foreignKey;} set{foreignKey = value;} } } |
Ø AutoIncreaseAttribute
AutoIncreaseAttribute指示该属性是自动增长的。自动增长默认种子为1。需要指出的是,自动增长的属性只适用于MS SqlServer,Websharp目前不支持其他数据库的自动增长列。
这个类的定义如下:
[AttributeUsage(AttributeTargets.Property)] public class AutoIncreaseAttribute : Attribute { private int step = 1; public AutoIncreaseAttribute(){} public AutoIncreaseAttribute(int step) { this.step = step; } public int Step { get{return step;} set{step = value;} } } |
设计好映射的方法后,我们就可以来定义实体类以及同数据库之间的映射。下面是一个例子:
//订单类别 [TableMap("OrderType","ID")] public class OrderType { private int m_ID; private string m_Name; [ColumnMap("ID",DbType.Int32)] public int ID { get { return m_ID; } set { m_ID = value; } } [ColumnMap("Name", DbType.String)] public string Name { get { return m_Name; } set { m_Name = value; } } } //订单 [TableMap("PurchaseOrder", "OrderID")] public class Order { private int m_OrderID; private OrderType m_OrderType; private string m_Title; private DateTime m_AddTime; private bool m_IsSigned; private List<OrderDetail> m_Details; [ColumnMap("OrderID",DbType.Int32)] [AutoIncrease] public int OrderID { get { return m_OrderID; } set { m_OrderID = value; } } [ReferenceObject(typeof(OrderType),"ID","TypeID")] [ColumnMap("TypeID",DbType.String)] public OrderType OrderType { get { return m_OrderType; } set { m_OrderType = value; } } [ColumnMap("Title", DbType.String)] public string Title { get { return m_Title; } set { m_Title = value; } } [ColumnMap("AddTime", DbType.DateTime)] public DateTime AddTime { get { return m_AddTime; } set { m_AddTime = value; } } [ColumnMap("AddTime", DbType.Boolean)] public bool IsDigned { get { return m_IsSigned; } set { m_IsSigned = value; } } [SubObject(typeof(OrderDetail),"OrderID","OrderID")] public List<OrderDetail> Details { get { return m_Details; } set { m_Details = value; } } } //订单明细 public class OrderDetail { private int m_DetailID; private int m_OrderID; private string m_ProductName; private int m_Amount; [ColumnMap("ID", DbType.Int32)] [AutoIncrease] public int DetailID { get { return m_DetailID; } set { m_DetailID = value; } } [ColumnMap("OrderID", DbType.Int32)] public int OrderID { get { return m_OrderID; } set { m_OrderID = value; } } [ColumnMap("ProductName", DbType.String)] public string ProductName { get { return m_ProductName; } set { m_ProductName = value; } } [ColumnMap("Amount", DbType.Int32)] public int Amount { get { return m_Amount; } set { m_Amount = value; } } } |
Order中
[ReferenceObject(typeof(OrderType),"ID","TypeID")] [ColumnMap("TypeID",DbType.String)] public OrderType OrderType { get { return m_OrderType; } set { m_OrderType = value; } } |
这段代码表明,OrderType这个属性,引用了OrderType这个对象,同OrderType相关联的,是OrderType的主键ID和Order的外键TypeID。
[SubObject(typeof(OrderDetail),"OrderID","OrderID")] public List<OrderDetail> Details { get { return m_Details; } set { m_Details = value; } } |
这段代码表明,Details这个属性,由子对象OrderDetail的集合组成,其中,两个对象通过Order类的OrderID主键和OrderDetail的外键OrderID相关联。
四. 对继承的支持
Websharp框架在设计的时候,要求能够支持面向对象语言中的继承。Websharp支持三种继承模式:ONE_INHERITANCE_TREE_ONE_TABLE、ONE_INHERITANCE_PATH_ONE_TABLE和ONE_CLASS_ONE_TABLE。以如下的类结构为例:
ONE_INHERITANCE_TREE_ONE_TABLE
这种映射模式将具有相同父类的所有类都映射到一个数据库表中。数据库结构如下图:
在Websharp中,只需要对每个类都指明具有相同值的TableMap特性就可以了。如下面的代码:
[TableMap("Table1", "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("Table1", "Property1")] public class Child1 : Parent { private string property3; [ColumnMap("Column3", DbType.String)] public string Property3 { get { return property3; } set { property3 = value; } } } [TableMap("Table1", "Property1")] public class Child2 : Parent { private string property4; [ColumnMap("Column4", DbType.String)] public string Property4 { get { return property4; } set { property4 = value; } } } |
此时,当按照如下的代码初始化一个Child1对象,
Child c1.Property1 = "P11"; c1.Property2 = "P12"; c1.Property3 = "P13"; |
并保存到数据库中的时候,数据库中的记录应该是:
Column1 |
Column2 |
Column3 |
Column4 |
P11 |
P12 |
P13 |
NULL |
如果按照如下的代码初始化一个Child2对象:
Child c2.Property1 = "P21"; c2.Property2 = "P22"; c2.Property4 = "P24"; |
并保存到数据库中的时候,数据库中的记录应该是:
Column1 |
Column2 |
Column3 |
Column4 |
P21 |
P22 |
NULL |
P24 |
ONE_INHERITANCE_PATH_ONE_TABLE
这种映射模式将一个继承路径映射到一个表,这种情况下的数据库的结构是:
这种情况下,实际上Parent类并不映射到实际的表,Child1和Child2类分别映射到Child1和Child2表。因此,在这种情况下,需要把Parent类的TableMap特性设置为Null,而Child1和Child2类的TableMap特性分别设置为Child1和Child2,代码如下面所示:
[TableMap(null, "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对象,
Child c1.Property1 = "P11"; c1.Property2 = "P12"; c1.Property3 = "P13"; |
并保存到数据库中的时候,数据库中应该只在Child1表中添加下面的数据:
Column1 |
Column2 |
Column3 |
P11 |
P12 |
P13 |
如果保存的是一个Child2对象,那么,应该只在Child2表中添加数据。Child1表和Child2表是互相独立的,并不会互相影响。
ONE_CLASS_ONE_TABLE
这种映射模式将每个类映射到对应的一个表,对于上面的类结构,数据库的结构是:
这种映射模式,我们只需要分别对每个类设定各自映射的表就可以了。代码如下面所示:
[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对象,
Child c1.Property1 = "P11"; c1.Property2 = "P12"; c1.Property3 = "P13"; |
并保存到数据库中的时候,数据库中的记录应该是:
Parent表:
Column1 |
Column2 |
P11 |
P12 |
Child1表:
Column1 |
Column3 |
P11 |
P13 |
同样的,如果保存的是一个Child2对象,那么,将在Parent表和Child2表中添加记录。
五. PersistenceManager
Websharp O/R Mapping框架的操作核心,在于PersistenceManager接口,对实体对象的操作,包括增、删、改的操作,都是通过这个接口来进行的。
PersistenceManager的定义如下:
/** * 这个接口定义持久化操作的方法 * */ public interface PersistenceManager : IDisposable { /* * 关闭一个PersistenceManager。当一个PersistenceManager被关闭的时候,需要做如下的工作 * 1、释放它所管理的实体类,以及相关的状态。如果不释放,可能造成内存泄露 * 2、如果Flush方法没有被执行,则执行Flush方法。 * 3、释放数据库联接等资源。 * 4、从PersistenManagerfactroy中清除自己 */ void Close(); /** * 判断一个PersistenceManager是否已经关闭了。 */ bool IsClosed{get;} /** * 获取PersistenceManager当前所处的事务 */ Transaction CurrentTransaction{ get;} /** * 指示在默认的情况下,所有的操作是否忽略缓存。如果忽略缓存,那么,有的时候会存在一些不一致的情况,除非系统被禁止了缓存的使用 */ bool IgnoreCache{get;set;} /** * 将一个新的实体对象转换成可持续对象,这个对象在事务结束的时候,会被Insert到数据库中 * 调用这个方法后,该对象的状态为EntityState.New * 如果一个对象的状态为EntityState.Persistent,则本方法将抛出一个EntityIsPersistentException异常 */ void PersistNew(object entity); void PersistNew(object entity,PersistOptions options); /* * 将一个新的实体对象转换成可持续对象,并且指明要保存的属性 * */ void PersistNew(object entity, params string[] properties); void PersistNew(object entity, PersistOptions options, params string[] properties); /** * 将一个实体对象保存到数据库中。 * 如果一个对象是Trasient的,则将其转换为EntityState.New状态。在事务结束的时候,会被Insert到数据库中 * 否则,其状态就是EntityState.Persist,就更新到数据库中。 * 如果一个Trasient对象实际上已经存在于数据库中,由于Persist方法并不检查实际的数据库,因此, * 调用这个方法,将会抛出异常。这个时候,应该使用先使用Attach方法,然后调用Persist。 * Persist方法主要用于已受管的对象的更新 */ 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); /** * 强制将一个实体更新到数据库中。 * 执行这个方法,将把对象的状态强制变为EntityState.Persist。 * 由于Update方法并不检查实际的数据库,因此如果一个对象实际上不存在于数据库中,那么,这个方法实际上 * 不会对数据库造成变化。 * 如果不能确认对象已经存在于数据库中,那么,应该使用先使用Attach方法。 */ void Update(object entity); void Update(object entity, PersistOptions options); /** * 将一个实体对象更新到数据库中,并且指明要保存的属性 * */ void Update(object entity, params string[] properties); void Update(object entity, PersistOptions options, params string[] properties); /** * 删除一个对象。 * 一个对象被删除后,其状态变成EntityState.Deleted,在事务结束的时候,会被从数据库中删除。 * 如果一个对象不是持久的,那么,这个方法将抛出异常。 * */ void Delete(object entity); void Delete(object entity,PersistOptions options); /** * 将一个对象标记为可持续的。如果这个对象已经存在于实际的数据库中,那么,这个对象的状态就是 * EntityState.Persistent,否则,这个对象的状态就是EntityState.New。 * 注意:这个方法不会导致对数据库实际数据的更改 */ void Attach(object entity); void Attach(object entity,PersistOptions options); /** * 重新从数据库中载入这个对象,这意味着重新给对象的各个属性赋值。 * */ void Reload(ref object entity); void Reload(ref object entity,PersistOptions options); /** * 从缓存中把某个对象移除。 * */ void Evict (object entity); void EvictAll (object[] pcs); void EvictAll (IEnumerable pcs); void EvictAll (); /** * 根据主键查找某个对象,如果主键是多个字段的,顺序必须同TableMapAttribute中的顺序相同,否则将有不可预测的事情发生 */ object FindByPrimaryKey(Type entityType,params object[] id); object FindByPrimaryKey(Type entityType, PersistOptions options, params object[] id); T FindByPrimaryKey<T>(params object[] id); T FindByPrimaryKey<T>(PersistOptions options, params object[] id); /** *获取某个对象的状态。这个对象必须是受该PersistenceManager管理的,否则,抛出异常 */ EntityState GetState(object entity); ICollection GetManagedEntities(); /** * 本操作使对实体对象所作的更改立即生效。 */ bool Flush(); /// <summary> /// 该方法撤销前面所做的所有操作。 /// </summary> void Cancel(); /** * 创建Query对象。 * */ Query NewQuery(); Query NewQuery(Type entityType); Query NewQuery(Type entityType,string filter); Query NewQuery(Type entityType,string filter,QueryParameterCollection paramColletion); Query<T> NewQuery<T>(); Query<T> NewQuery<T>(string filter); Query<T> NewQuery<T>(string filter, QueryParameterCollection paramColletion); } |
PersistenceManager接口的相应的方法的说明已经在上面列出了,也是一目了然的。额外需要说明的是,这些对对象的操纵,都是暂存在内存中,只有在调用了Flush方法后才生效,才会把对数据的改变保存到数据库中。调用Cancel方法,可以撤销这些操作。
六. 事务处理
Websharp框架提供了简单的事务处理的能力,这是通过Transaction接口来实现的。
Transaction接口的定义如下:
public interface Transaction { void Begin(); void Commit(); void Rollback(); PersistenceManager PersistenceManager{get;} } |
这个接口定义的非常简单,就是提供了开始事务、提交或者回滚事务的方法。下面的例子说明了在Websharp里面如何使用事务:
public void TransactionTest() { //设定环境 DatabaseProperty dbp = new DatabaseProperty(); dbp.DatabaseType = DatabaseType.MSSQLServer; dbp.ConnectionString = "Data Source=(local);Initial Catalog=WebsharpTest;Integrated Security=True"; //操作数据 PersistenceManager pm = PersistenceManagerFactory.Instance().Create(dbp); Product p0 = new Product("Name0", 100); pm.PersistNew(p0); Product p1 = new Product("Name1", 101); pm.PersistNew(p1); Transaction t = pm.CurrentTransaction; //获取当前事务对象 t.Begin(); //开始事务 pm.Flush(); t.Commit(); //提交事务 pm.Close(); } |
七. 对象查询
Websharp框架通过Query接口,提供对象查询的功能。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;} ICollection QueryObjects(); DataSet QueryDataSet(); PersistenceManager PersistenceManager{get;} bool IsClosed{get;} void Close(); //void Open(); } |
同时,Websharp提供了一个范型的版本,定义如下:
public interface Query<T> : Query { new ICollection<T> QueryObjects(); } |
使用Query的一般步骤是:
1、 从PersistenceManager获得一个Query接口
2、 设定Query接口要查询的类型、条件(Filter)和相应的参数
3、 执行QueryObjects方法,得到相应的对象集合。
下面的例子说明了使用Query的过程:
public void NormalQuery() { DatabaseProperty dbp = new DatabaseProperty(); dbp.DatabaseType = DatabaseType.Oracle; dbp.ConnectionString = "Data Source=ORACLE1453;User Id=websharp;Password=websharp"; ApplicationConfiguration.FrameworkUssage = FrameworkUssage.Normal; ApplicationConfiguration.DefaultDatabaseProperty = dbp; PersistenceManager pm = PersistenceManagerFactory.Instance().Create(); Query<Pencil> q = pm.NewQuery<Pencil>(); q.Filter = "Owner like :Owner"; QueryParameterCollection qpm = new QueryParameterCollection(1); QueryParameter qm = new QueryParameter(":Owner", "sunny"); qpm.Add(qm); q.Parameters = qpm; ICollection<Pencil> pencils = q.QueryObjects(); pm.Close(); } |
八. 配置文件
配置文件配置了Websharp运行时需要的一些信息,目前来说,在O/R Mapping部分,需要配置的是关于缓存的信息。在缓存部分,Websharp使用了微软的Enterprise工具中的缓存部分,因此,需要配置的信息同微软的Enterprise工具中的缓存部分一样。所需要添加的,就是缓存过期的策略部分,这个部分,在WebsharpExpirationPolicy中进行说明。配置文件的内容如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version= <section name="WebsharpExpirationPolicy" type="Websharp.ORM.Service.WebsharpCofigurationHandler,Websharp.ORM.Service" /> </configSections> <cachingConfiguration defaultCacheManager="Cache Manager"> <cacheManagers> <add expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="Null Storage" name="Cache Manager" /> <add expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="Null Storage" name="EntityCache" /> <add expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="Null Storage" name="SqlCache" /> </cacheManagers> <backingStores> <add encryptionProviderName="" type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version= name="Null Storage" /> </backingStores> </cachingConfiguration> <WebsharpExpirationPolicy> <ExpirationPolicy ExpirationCheckInterval="60" AssemblyName="Microsoft.ApplicationBlocks.Cache" ClassName="Microsoft.ApplicationBlocks.Cache.ExpirationsImplementations.SlidingTime" /> </WebsharpExpirationPolicy> </configuration> |
Websharp默认使用的是时间过期清除缓存的策略,过期的时间,可以修改,单位为秒。
有一点另自己不满意的是,在配置文件部分,我依然使用了.Net Framewoork1.1时期的配置文件操作方式,不过这对系统并无丝毫影响。
九. 后记
设计Websharp时,在考虑映射方法的时候,曾经为使用XML文件还是Attribute来映射考虑了很长时间,最终还是选择了Attribute。用XML文件来作为映射手段,虽然这种使用方法很常见,但是,感觉使用起来非常不方便,我个人也时常为EJB和Hibernate里面的配置文件烦恼不已,而使用Attribute,明显感觉清爽了很多。这似乎也是未来的一个方向,这从EJB3.0的变化也可以看出。
Websharp O/R Mapping的结构设计,参考了JDO的很多东西,感觉在编程思想和面向对象方法的使用和讨论方面,Java阵营真是活跃的多。实际上,对于一个程序员来说,在对数个不同的技术都有了深入的了解以后,会发现自己对软件系统开发的看法有一种融会贯通的感觉。实现是一个方面,但同样重要的是结构。这两年,一直在同时做Java和.Net的开发,很多时候是跨两个平台的开发,从两个平台中都收获不少。没有感觉Java或者C#对一个程序员来说有什么区别。那些关于Java和C#孰优孰劣的争论真是没有必要,也很肤浅。当然,C#作为一种后出的语言,在语法设计上的确有不小精妙的地方,这也是我崇拜Anders Hejlsberg的原因,也是我使用C#来实现Websharp的原因。但对于一个系统的开发来说,这种语言上的差异是可以忽略不计的。语言不是重要的,重要的是编程的思想。
一个人开发一个框架,是一件累人的事情,有的时候自己几乎就要放弃。现在虽然终于有了一个阶段的成果,但是,有些地方可能没有经过严格的测试,因此,希望各位同仁在使用的过程中,如果发现问题,能够及时告诉我,以便于我及时修正问题。也希望有人如果感兴趣,我们一起来发展这个项目。