The .NET ORM Architecture(.Net ORM 架构)
一、Grove描述
Grove是为.Net项目开发而设计的一个组件。Grove ORM Development Toolkit包含包含Grove和Toolkit两部分内容,Grove基于.Net框架,支持多数据,提供标准的拖曳、三层及多层的开发模式。
二、Grove工具包
Grove工具是一个基于.Net开发环境的附件,它能够从数据源直接查看到实体类与XML描述。例如单一对象或者关系对象(互相联系的对象),这里的几个抓图屏幕显示了它的一些描述。
三、The ObjectOperator
The ObjectOperator 与数据进行通信,它使用 AppSettingManager 来设置数据源,如设置数据库服务器的连接字符串。The ObjectOperator 使用Attributes决定在数据源中的字段与表与一个对象成员的映射关系,即字段与表做为一个持久性对象来使用与从数据源中返回一个对象成员属性。
The ObjectOperator 可能使用一个存在连接字符串来构造,或者你必须确保在当前项目的Web.Config文件中的ConfigurationSettings存在AppSettings节点,并设置Key属性值为"DBConnString",Value值为一个正确的连接字符串。
下面的例子显示了ObjectOperator的创建:
[System.Configuration.ConfigurationSettings.AppSettings["DBConnString"];]
ObjectOperator oo=new ObjectOperator();
[Custom Connection String]
ObjectOperator oo=new ObjectOperator("Server=localhost;Uid=sa;Pwd=sa;Database=sample");
ObjectOperator提供了下列方法,主要功能是从一个数据源中返回对象,或者返回给数据源对象。
Method | Description |
Insert | Insert an object into the data source |
Update | Update an object |
Remove | Delete an object from the data source |
RemoveChilds | Delete child objects |
Retrieve | Returns an object from the data source |
RetrieveChilds | Returns child objects from the data source |
GetDataReader | Returns an IDataReader from the data source |
GetObjectSet | Returns a collection of object values |
GetObjectSource | Returns a DataSet which contain the data source of the object |
GetCount | Returns a records count of the data source |
BeginTranscation | Begins a transaction at the data source, if transactions are supported by the data source. |
Commit | Commits the current transaction. |
Rollback | Rolls back the current transaction. |
四、The ObjectQuery
这ObjectQuery被用来帮助ObjectOperator从数据源中获得对象,例如,ObjectOperator需要它来得到一个“QueryString”而加以执行,ObjectQuery使用Attributes关键字决定当前对象引用单表或多表。
一个ObjectQuery对象的构造,通过传递一个对象类型或一个过滤字符串给ObjectQuery的构造函数,详细过滤定义,参考Filter syntax (过滤语法)。例如,下面的ObjectQuery搜索所有State值等于“WA”的Customer对象。
ObjectQuery query=new ObjectQuery(typeof(Customer),"this.State='WA'");
为返回对象的所有数据类型,指定一个空的字符中作为你的过滤条件,如下例子:
ObjectQuery query=new ObjectQuery(typeof(Customer),"");
这Filter 允许你在关系对象中使用“Contains”关键字定义字查询,你可以查询出存在定单数据超过50的
所有Customer对象。
ObjectQuery query = new ObjectQuery(typeof(Customer),”Order.CustomerID.Contains(this.CustomerID)”);
query.DeclareSubset(typeof(Order),”Order.Quantity>50”);
五、The FilterExpression(过滤表达式)
这FilterExpression 是一个可扩展的过滤,即FilterExpression允许你偏离ObjectQuery来为一些操作构造许多复杂的条件,例如通过自定义条件来更新一个对象。
一个FilterExpression的创建,通过传递一个对象类型或传递一个过滤字符串给ObjectQuery的构造函数。
例如下面的FilterExpression定义了一个State等于“WA”的所有“Customer类型”的对象的过滤表达式。
FilterExpression filterex = new FilterExpression(typeof(Customer),”this.State=’WA’”);
这Filter 允许你在关系对象中使用“Contains”关键字定义字查询,你可以查询出存在定单数据超过50的所有Customer对象。
FilterExpression filterex=new FilterExpression(typeof(Customer),"Order.CustomerID.Contains(this.CustomerID)");
filterex.DeclareSubset(typeof(Order),"Order.Quantity>50");
有时,我们需要更新一个对象的属性,而不更新其它属性。例如,仅仅需要更新Customer对象中State属性值,通过自定条件,如下所示:
ObjectOperator oo=new ObjectOperator();
oo.Update(typeof(Customer),filerex,"this.Status=1");
这个例子意思是将定单数量(Order)大于50的所有客户(Customer)的“State”的值设为1。
Persisting Objects (持久性对象)
一、Mapping Object Properties(映射对象属性)
这Grove ORM architecture 要求每一个持久性对象包含一个属性,这个属性值表示一个来自数据源的表的名字,此表名标示符在Object Mapping (对象映射)中用DataTable属性关键字来表示。
当从一个数据源中映射过来时,这PK(主键字段)需要一个属性,来表示此字段为主键字段,例如,
[KeyField("CustomerID")]
public int CustomerID{get; set;}
如果这PK Field(主键字段)不唯一,你必须确保这“KeyType”是“UniquelType.OtherDefinition”,下面的例子表示了字段类型是一个数据不唯一的String(字符串)类型。
[KeyField("Field Name",KeyType=UniqueIDType.OtherDefinition)]
public string PropertyName{get; set;}
并且,这PK field也可以使用ForeignKeyField属性来表示来自数据源的一个字段名(外键)。、
[DataTable("Orders")]
public class Order
{
[ForeignKeyField("CustomerID")]
public int CustomerID{get; set;}
}
另外,其它字段也需要一个名为DataField的属性来表示来自数据源的表的字段。
[DataField("Field Name")]
public type PropertyName{get; set;}
当将数据源(表)映射成为对象时,你需要量将the .NET framework data provider data types 映射成为NET framework data types。
下面的表显示了.NET Framework type与Microsoft SQL Server, OLE DB, and ODBC.的比较。详细信息请参考.NET Framework Developer's Guide。
注意:在.NET Framework data provider data types下的Null值被取代为DBNull.Value。
.NET Framework Data Provider for SQL Server
SQL Server type | .NET Framework type |
bigint | Int64 |
binary | Byte[] |
bit | Boolean |
char | String Char[] |
datetime | DateTime |
decimal | Decimal |
float | Double |
image | Byte[] |
int | Int32 |
money | Decimal |
nchar | String Char[] |
ntext | String Char[] |
numeric | Decimal |
nvarchar | String Char[] |
real | Single |
smalldatetime | DateTime |
smallint | Int16 |
smallmoney | Decimal |
sql_variant | Object * |
text | String Char[] |
timestamp | Byte[] |
tinyint | Byte |
uniqueidentifier | Guid |
varbinary | Byte[] |
varchar | String Char[] |
.NET Framework Data Provider for OLE DB
OLE DB type | .NET Framework type |
DBTYPE_I8 | Int64 |
DBTYPE_BYTES | Byte[] |
DBTYPE_BOOL | Boolean |
DBTYPE_BSTR | String |
DBTYPE_HCHAPTER | Supported through the DataReader |
DBTYPE_STR | String |
DBTYPE_CY | Decimal |
DBTYPE_DATE | DateTime |
DBTYPE_DBDATE | DateTime |
DBTYPE_DBTIME | DateTime |
DBTYPE_DBTIMESTAMP | DateTime |
DBTYPE_DECIMAL | Decimal |
DBTYPE_R8 | Double |
DBTYPE_ERROR | ExternalException |
DBTYPE_FILETIME | DateTime |
DBTYPE_GUID | Guid |
DBTYPE_IDISPATCH * | Object |
DBTYPE_I4 | Int32 |
DBTYPE_IUNKNOWN * | Object |
DBTYPE_NUMERIC | Decimal |
DBTYPE_PROPVARIANT | Object |
DBTYPE_R4 | Single |
DBTYPE_I2 | Int16 |
DBTYPE_I1 | Byte |
DBTYPE_UI8 | UInt64 |
DBTYPE_UI4 | UInt32 |
DBTYPE_UI2 | UInt16 |
DBTYPE_UI1 | Byte |
DBTYPE_VARIANT | Object |
DBTYPE_WSTR | String |
DBTYPE_UDT | not supported |
DBTYPE_VARNUMERIC | not supported |
.NET Framework Data Provider for ODBC
ODBC type | .NET Framework type |
SQL_BIGINT | Int64 |
SQL_BINARY | Byte[] |
SQL_BIT | Boolean |
SQL_CHAR | String |
SQL_DECIMAL | Decimal |
SQL_DOUBLE | Double |
SQL_GUID | Guid |
SQL_INTEGER | Int32 |
SQL_LONG_VARCHAR | String |
SQL_LONGVARBINARY | Byte[] |
SQL_NUMERIC | Decimal |
SQL_REAL | Single |
SQL_SMALLINT | Int16 |
SQL_TINYINT | Byte |
SQL_TYPE_TIMES | DateTime |
SQL_TYPE_TIMESTAMP | DateTime |
SQL_VARBINARY | Byte[] |
SQL_WCHAR | String Char[] |
SQL_WLONGVARCHAR | String Char[] |
SQL_WVARCHAR | String Char[] |
下面的代码,表示一个简单的一个映射关系:
[DataTable("Customers")]
public class Customer
{
int customerID;
string customerName;
int parentID;
....
[KeyField("CustomerID")]
public int CustomerID
{
get{return this.customerID;}
}
[DataField("CustomerName")]
public string CustomerName
{
get{return this.customerName;}
set{this.customerName=value;}
}
[ForeignKeyField("ParentID")]
public int ParentID
{
get{return this.parentID;}
set{this.parentID=value;}
}
}
二、Persisting Object Data(持久性对象数据)
ObjectOperator 为每一个对象提供了基本的持久性方法,比如insert、insert, update, delete以及从一个数据源返回一个对象,或者通过RetriveChilds,GetObjectSet等方法来获得一个相关的对象。
因此,程序员可以扩展这些方法为更多条件的选择,下面的代码显示了它的用法。
[DataTable("Customers")]
public class Customer
{
int customerID;
...
ArrayList orders=null;
[KeyField("CustomerID")]
public int CustomerID
{
get{return this.customerID;}
}
public ArrayList Orders
{
get{
if(orders==null && customerID>0)
orders=(new ObjectOperator()).RetrieveChilds(typeof(Order),customerID)
return orders;
}
}
}
下面的例子给出了一些基本的用法:
[persist a new object ]
Customer c=new Customer();
c.CustomerName="rainbow co.";
oo.Insert(c);
[update an object]
c.CustomerID=1000;
c.CustomerName="rainbow-co.";
oo.Update(c);
[update an retrieved object]
Customer c=(Customer)oo.Retrieve(typeof(Customer),1000);
c.CustomerName="rainbow.co"
oo.Update(c);
[update an object with new regulation]
Product p=(Product)oo.Retrieve(tyoeof(Product),guidString);
p.ProductID=newGuidString;
oo.Update(p,"this.ProductID=guidString");
Note the existing KeyField type is UniqueIDType.OtherDefinition,and need to update that.
[update objects perproty without other properties ]
oo.Update(typeof(Customer),"this.CustomerID<1000","this.Status=2");
Note update status to 2 for customer objects with id small than 1000.
[delete an object]
oo.Remove(c);
[delete related child objects]
int customerID=1000;
oo.RemoveChilds(typeof(Order),customerID);
Note the Order object must be contain a ForeignKeyField attribute for the FK field(CustomerID
三、Retrieve Object Data(返回一个对象数据)
ObjectOperator提供了极其丰富的方式,通过ObjectQuery来返回一个对象或结果集合,详细信息请查看Query for objects。
下面的例子显示了它的基本用法:
[return an existing object]
Customer c=(Customer)oo.Retrieve(typeof(Customer),1000);
[return a related child collection]
ArrayList orders=oo.RetrieveChilds(typeof(Order),c.CustomerID);
[return a related child collection through ObjectQuery]
ArrayList orders=oo.GetObjectSet(new ObjectQuery(typeof(Order),"this.CustomerID="+c.CustomerID));
[return DataSet]
EntityData orders= oo.GetObjectSource(new ObjectQuery(typeof(Order),"this.CustomerID="+c.CustomerID));
Note EntityData is an object extends the DataSet from the System.Data namespace
四、Grove Transaction(事务)
这Grove 架构支持基本的事务处理方法,通过ObjectOperator对象下的BeginTransaction、Commit和Rollback方法。如果你的数据源支持事务,你可以使用这些方法。或者你也可以有选择支持isolation level(隔离级别), 通过从System.Data命令空间里使用IsolationLevel 枚举值。如果你不使用Isolation level,缺省使用ReadCommitted事务级别。
注意:引用System.Data命名空间。
oo.BeginTranscation();
try{
oo.Insert(c);
oo.Commit();
}
catch{
oo.Rollback();
}
Querying for objects
一、Mapping Relation Object Properties
Grove 架构支持映射多表到一个对象——关系对象被用来做更复杂的查询,关系对象映射制授权你在两表之间指定连接类型,即你可以有选择地使用inner join、left outer join 、right outer join或者full join为一个属性,被用在表间的映射关系。
在映射时,关系必须包含一个成员,显示结果名(执行返回一个数据集)并指定FROM子句,在此成员的属性BeginWithTable中没有FROM关键字。下面的代码表明了怎样将多表映射成为一个关系对象,从数据源中选择需要返回的字段为这个对象。
[RelationTable("PersonRelationQuery",BeginWithTable="Person")]
public class PersonInfo
{
[RelationReflect("Person","Address",JoinType=TableJOINType.LEFTOUTERJOIN)]
[RelationField("Id","PersonID")]
public string Relationship_1
{
get{return "[Person].[Id]=[Address].[PersonID]";}
}
int _Id;
[DataField("Id",TableName="Person")]
public int PersonId
{
get{return this._Id;}
set{this._Id=value;}
}
string _Name;
[DataField("Name",TableName="Person")]
public string PersonName
{
get{return this._Name;}
set{this._Name=value;}
}
string _Street;
[DataField("Street",TableName="Address")]
public string StreetInfo
{
get{return this._Street;}
set{this._Street=value;}
}
}
Grove Toolkit中Relation Query Builder能够帮助你,随意映射。
二、Creating Object Queries
Object queries通过创建一个ObjectQuery 对象实例指定。关于ObjectQuery的更多细节,请查看The Object Query Overview.。
[no filter query]
ObjectQuery query=new ObjectQuery(typeof(Customer),"");
[filter query]
ObjectQuery query=new ObjectQuery(typeof(Customer),"this.State='WA'");
[object oriented syntax filter]
ObjectQuery query=new ObjectQuery(typeof(Customer),"this.State='WA' && this.Country=='USA'");
[sub-set with filter query]
ObjectQuery query=new ObjectQuery(typeof(Customer),"Order.CustomerID.Contains(this.CustomerID)"); query.DeclareSubset(typeof(Order),"Order.Quantity>50");
[sub-set without filter query]
ObjectQuery query=new ObjectQuery(typeof(Customer),"PersonInfo.PersonId.Contains(this.CustomerID)");
query.DeclareSubset(typeof(PersonInfo));
ObjectQuery允许用户使用AddCandidate方法来定义使用数据库函数,如COUNT、SUM、MAX、MIN等,下面例子显示了它们基本用法。
[count query]
ObjectQuery query=new ObjectQuery(typeof(Customer),"this.CustomerName<>''");
query.AddCandidate("this.CustomerID.size()");
NOTE the same as use query.AddCandidate("*.size()");
[sum query]
ObjectQuery query=new ObjectQuery(typeof(Person));
query.AddCandidate("this.Age.sum()");
[maximum query]
ObjectQuery query=new ObjectQuery(typeof(Person));
query.AddCandidate("this.Age.max()");
[minimum query]
ObjectQuery query=new ObjectQuery(typeof(Person));
query.AddCandidate("this.Age.min()");
[average query]
ObjectQuery query=new ObjectQuery(typeof(Person));
query.AddCandidate("this.Age.avg()");
四、Returning Objects
IDataReader reader=oo.GetDataReader(new ObjectQuery(typeof(Customer),""));
ArrayList customers=oo.GetObjectSet(new ObjectQuery(typeof(Customer),""));
EntityData result=oo.GetObjectSource(new ObjectQuery(typeof(Customer),""));
五、Using Filter
IDataReader reader=oo.GetDataReader(new ObjectQuery(typeof(Customer),"this.State='WA'"));
关于更多信息,请查看The ObjectQuery and The FilterExpression。
六、Using Sub-set for queries
ObjectQuery允许你定义子集查询,即可以使用“IN”或“NOT IN”来查询,需要使用Contains关键来。
[syntax]
Object.Property.Contains(this.Property)
Note NOT IN query need contain "!" before the head.
ObjectQuery query=new ObjectQuery(typeof(Customer));
query.Filter="Order.CustomerID.Contrains(this.CustomerID)";
query.DeclareSubset(typeof(Order),"Order.Quantity>50");
ArrayList customers=oo.GetObjectSet(query);
七、Filter Syntax
Filter是ObjectOperator被用来查询对象一个查询语言,Filter允许你使用标准面向对象语言的关系操作符来查询对象。在一个查询中你可以遍历对象的关系,也可以使用标准的面向对象关系操作符进行复杂的值比较。
Operator | Description |
!, not | Used to perform a Boolean operation on a single value. For example: !Order.CustomerID.Contains(Customer.CustomerID) |
<, >, <= , >= | Used to compare one value to another. For example: Order.Quantity >= 12 |
=, !=, <>, = = | Used to compare exact values. For example: Customer.Country = 'USA' and Customer.Region != 'WA' |
and, && | Used to perform a logical junction. For example: Customer.Country = 'USA' and Customer.Region = 'WA' |
or, || | Used to perform a logical disjunction. For example: Customer.LastName = 'Smith' or Customer.LastName = 'Jones' |
八、Order String(排序字符)
Order String 允许你在返回对象时控制排序。
[desc]
ObjectQuery query=new ObjectQuery(typeof(Customer),"");
query.OrderString="this.CustomerID desc";
[multi order condition]
query.OrderString="this.CustomerName,this.GetDate desc"