.Grove—— .Net下的ORM框架
參見原文:http://grove.91link.com(英)
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>
五、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能夠幫助你,隨意映射。
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=='
[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()");
IDataReader reader=oo.GetDataReader(new ObjectQuery(typeof(Customer),""));
ArrayList customers=oo.GetObjectSet(new ObjectQuery(typeof(Customer),""));
EntityData result=oo.GetObjectSource(new ObjectQuery(typeof(Customer),""));
IDataReader reader=oo.GetDataReader(new ObjectQuery(typeof(Customer),"this.State='WA'"));
關於更多信息,請查看The ObjectQuery and The FilterExpression。
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是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 = ' |
and, && | Used to perform a logical junction. For example: Customer.Country = ' |
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"