一.XPO简介:
XPO即eXpress Persistent Objects for .NET,现在这里介绍的版本是1.5。
XPO在应用程序代码和数据库之间扮演了一个中间层的角色,简单而言,就是将面向对象编程所建立的对象在数据库中做一个映射,使之和数据库中的表建立一一对应的关系。XPO现在的版本已经可以处理对象之间一对多,多对多等关系的处理,继承在数据库中的实现以及根据情况处理进行数据库的并发访问,提高系统的效率。
XPO最大的好处就是可以将应用程序开发者从繁琐的数据库设计中解脱出来,专心建立业务对象系统,如何将这些对象映射到数据库中,这一切都可以交给XPO来自动处理了。
二.XPO的简单使用:
XPO的使用十分简单,如果是应用系统的数据关系不复杂,对于应用开发人员而言,根本上可以不了解XPO太深入的技术,只需要在建立对象类过程中,遵循一定的规则就可以了。而且在使用过程中也根本可以不去关心数据库后台发生的事情,例如后台是什么数据库,表到底有多少等。
1.建立对象类:
首先是需要定义对象,一切的对象只需要继承于XPObject即可。
例如下面的代码就是建立一个Member的类,其中涉及到名字和年龄。
这个是一个最简单的例子,在使用过程中根本不需要考虑数据库到底建立了没有,以及数据库建立在那里等。当然,如果在应用中需要了解这些信息,在后面的介绍中可以看到,其实XPO同样提供了十分丰富的函数来获取这些信息的。
using DevExpress.Xpo;
namespace testXPO
{
/// <summary>
/// Member 的摘要说明。
/// </summary>
public class Member : XPObject
{
public string strName;
public int iAge;
public Member()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
}
}
2.使用和保存这些对象的数据:
以下代码是定义一个Member,并且将这些数据保存起来的例子:
m.iAge = 15;
m.strName = "testName";
m.Save();
可以看到使用起来十分方便,一点也看不到数据库操作的影子。
3.获取对象的集合信息:
保存的信息如何全部获取呢?以下是个例子:
{
lstResult.Items.Insert(0,"Age:" + m.iAge + "; Name:" + m.strName);
}
假设将全部Member的信息显示在一个ListBox中。
小结:到此为止对于如何使用XPO应该有个很初步的了解了。
步骤大致如下:
1. 将DevExpress.XPO.dll引用进来;
2. 声明使用 using DevExpress.XPO;;
3. 需要数据保存的类则继承于XPObject;
4. 需要对外的信息则定义成字段即可,一切象一般创造一个类无异;
XPO到底做了些什么呢?
面对这么简单的代码,其实和数据库的互动已经全部由XPO自动帮你完成了。
1. 运行后会在当前目录下建立一个<AppName>.MDB的数据文件;
2. 打开这个数据文件可以发现有两个系统自动建立的表:XPDeletedObject和XPObjectType;
3. 另外还有就是对应类的表了,这里是Member表。
在MDB中的结构如下图所示:
到此为止我们已经可以用XPO来完成新建表,新建记录,返回全部记录这些传统的数据库操作了。虽然功能十分简单,但是可以大致了解XPO大致会为我们做些什么了,会给我们带来一些什么的方便了。
三.XPO的进一步使用:
(一)对象关系的处理:
XPO支持对象之间一对一,一对多和多对多的关系。
1.定义一对多的关系:
假设有一个Student的类和一个Class类是一对多的对应关系,一个学生可以有多个Class的信息。
类图关系如下:
Class类对应的代码:
using DevExpress.Xpo;
namespace testXPO
{
/// <summary>
/// Class 的摘要说明。
/// </summary>
public class Class : XPObject
{
[Association("StudentClasses")]
public Student Student;
public string Name;
public int Credit;
public Class()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
}
}
Student类对应的代码
using DevExpress.Xpo;
namespace testXPO
{
/// <summary>
/// Student 的摘要说明。
/// </summary>
public class Student : XPObject
{
public string Name;
public int Age;
[Association("StudentClasses", typeof(Class))]
public XPCollection Classes { get { return GetCollection("Classes"); } }
public Student()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
}
}
其中Association属性部分就是关键定义这个一对多关系的代码。
以下代码是添加记录的代码,假设添加5个学生,每个学生有不同的三门课程。
{
Student sd = new Student();
sd.Name= "Student" + i.ToString();
sd.Age = 2*i;
Class cls1 = new Class();
Class cls2 = new Class();
Class cls3 = new Class();
cls1.Name = sd.Name + "英语";
cls1.Credit = 5 * sd.Age;
sd.Classes.Add(cls1);
cls2.Name = sd.Name + "数学";
cls2.Credit = 6 * sd.Age;
sd.Classes.Add(cls2);
cls3.Name = sd.Name + "语文";
cls3.Credit = 7 * sd.Age;
sd.Classes.Add(cls3);
sd.Save();
}
可以看出:
Class数据不需要单独保存,在后面总体对Student做一个保存,XPO会自动完成CLASS数据的保存;对于一个学生对应几个CLASS,每个CLASS的实体需要存在,例如这里就新建了cls1-cls3三个不同的实体,否则保存到数据库中,之后最后一个实体有效。
那么在数据库中发生了什么事情呢:
CLASS和STUDENT两个表自动生成了,且数据也保存了,之间的关系也建立了,如下图:
以下是对这种关系的数据进行浏览,例如想获取学分是5的信息:
foreach (Class cls in clsAll)
{
lstResult.Items.Insert(0,"Student's Name: " + cls.Student.Name +
";Student's Age:" + cls.Student.Age + "; Class's Name:" + cls.Name);
}
由此可见XPCollection可以作为对象的集合来使用,且对应回数据库中的记录概念,当然这种对象的集合比数据表的记录灵活很多。
小结:对于关系,一方的关系定义象Student中:
public XPCollection Classes { get { return GetCollection("Classes"); } }
而多方中的关系定义象Class中:
public Student Student;
也就是说一方定义需要集合类型(多),而多方定义则只须定义回一方的类。
注意:Association中定义的关系名字是相同的。
(二)对象继承的处理:
继承的例子:例如学校中有Staff和Dean类,Dean从Staff继承过来,当你查找Staff的数据时,Dean的数据也会获取出来的。
定义Staff的代码:
using DevExpress.Xpo;
namespace testXPO
{
/// <summary>
/// Staff 的摘要说明。
/// </summary>
public class Staff : XPObject
{
public string Name;
public int Age;
[Association("DeanStaffs")]
public Dean Dean = null;
public Staff()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
}
}
定义Dean的代码:
using DevExpress.Xpo;
namespace testXPO
{
/// <summary>
/// Dean 的摘要说明。
/// </summary>
public class Dean : Staff
{
public string Department;
[Association("DeanStaffs", typeof(Staff))]
public XPCollection Staffs
{
get { return GetCollection("Staffs"); }
}
public Dean()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
}
}
可见继承关系除了面向对象本身定义了的继承外,还需要声明之间的关系,这个关系就是对应到数据库中的关系了。
以下是生成数据的代码:
{
Dean d = new Dean();
d.Name = "Dean" + i.ToString();
d.Age = 100 * i;
d.Department = "Dean's Department" + i.ToString();
d.Save();
}
for (int j=1;j<20;j++)
{
Staff s = new Staff();
s.Name = "Staff" + j.ToString();
s.Age = j;
s.Save();
}
前面部分生成Dean的数据,后面生成Staff的数据,那么数据库里面发生了什么变化呢:
生成的数据如下图:
这里可以看到Dean中的OID不再是自动生成的了,而是对应Staff中的OID。
小结:
对于父类,需要声明关系如Staff:
public XPCollection Staffs
{
get { return GetCollection("Staffs"); }
}
对于子类,需要声明关系如Dean:
public Dean Dean = null;
(三)XPO中Session的使用:
XPO的缺省设置是使用MS Access OLEDB,并且使用在当前路径下的MDB,如果应用需要特别指明数据库则需要用到Session了。
只需要在对象构造时将Session作为参数传递进去就可以了。
例如:
{
public string strName;
public int iAge;
public Member(Session session) : base (session)
{
//
// TODO: 在此处添加构造函数逻辑
//
}
}
创建Session则如下面的代码:
session2.AutoCreateOptions = AutoCreateOptions.SchemaOnly;
session2.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;
User ID=Admin;Data source=SessionExample2_cs.mdb;Mode=Share Deny None;";
session2.Connect();
(四)XPO中查询数据:
对于查询,就是指定一定的条件获取一个数据集合。在XPO中,返回结果的集合用XPCollection作统一处理,而条件查询的条件设置,则通过
BinaryOperator来指定。
例如:要查询出生日期在”01/02/1960”年之前的客户资料:
new BinaryOperator("BirthDate", new DateTime(1960, 1, 2), BinaryOperatorType.Less))
如果条件是组合的,例如:要查询出生日期在”01/02/1960”年之前的且地址是第十大街的顾客的资料:
criteria.Operands.Add(new BinaryOperator("BirthDate", new DateTime(1960, 1, 2), BinaryOperatorType.Less));
criteria.Operands.Add(new BinaryOperator("Address.Street", "10'th Avenue"));
new XPCollection(typeof(Customer), criteria)
(五)XPO中的事务处理:
XPO提供了类似于Microsoft ADO.NET中的事务处理,你可以在Session中使用Begin,Commit或者Rollback的方法来完成处理。在当前的版本中,事务处理的嵌套并不支持。
以下代码是事务处理的例子:
public double Amount = 0;
protected override void BeforeSave() {
base.BeforeSave();
if (Amount < 0) {
throw new Exception("Negative amount");
}
}
}
void TransferAmount(double amount) {
Account account = new Account();
Session.DefaultSession.BeginTransaction();
try {
account.Amount = amount;
account.Save();
Session.DefaultSession.CommitTransaction();
}
catch (Exception e) {
Session.DefaultSession.RollbackTransaction();
account.Reload();
}
}
(六)XPO中返回记录的分页处理:
在XPO中,除了XPCollection外,还有一个XPCursor提供了一个获取返回记录的另外一种方式。和XPCollection返回全部的满足条件的集合不同,XPCursor根据XPCursor.PageSize的设置依次返回满足条件的每页数据。
XPCursor这种特性在处理数据量比较大的表是十分有用的,可以减少每次内存的使用量和减少返回的时间。
当然,每次翻页则需要重新获取数据。
(七)XPO中对结构体的支持:
从1.5版本开始,XPO将支持结构的特性。
例如定义一个圆的类,圆心是一个结构体,代码如下:
using DevExpress.Xpo;
namespace testXPO
{
/// <summary>
/// Circle 的摘要说明。
/// </summary>
public struct Point
{
[Persistent("Abscissa")]
public int X;
public int Y;
}
public class Circle : XPObject
{
public string Name = "";
[Persistent("Location")]
public Point Center;
public Circle()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
}
}
生成数据的代码如下:
{
Circle c = new Circle();
c.Name = "Circle" + i.ToString();
c.Center.X = 100 + i;
c.Center.Y = 10 + i;
c.Save();
}
看看数据库中表的存储情况:
小结:XPO对结构体已经支持,并且将其平面化到同一个表中。
?? 不知道对结构体嵌套的是否支持?有待证实
对于结构体相关数据的查询也并不复杂,例如下面: