PDF.NET SOD框架不只仅是一个ORM,可是它的ORM功能是独具特点的,我在博客中已经屡次介绍,但都是原理性的,可能很多初学的朋友仍是以为复杂,其实,SOD的ORM是很简单的。下面咱们就采用流行的 Code First的方式,一步步来了解下。html
首先创建一个控制台项目(支持.NET2.0的项目),并使用程序包管理器添加PDF.NET SOD的程序引用:前端
PM> Install-Package PDF.NET.SODgit
更多详细使用信息说明,请参考nuget 网站说明 https://www.nuget.org/packages/PDF.NET/github
新建一个控制台项目,添加一个应用程序配置文件,增长一个数据库链接配置:sql
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="local" connectionString="Data Source=.;Initial Catalog=LocalDB;Integrated Security=True" providerName="SqlServer" /> </connectionStrings> </configuration>
上面的链接字符串要求你本地已经安装SqlServer,框架兼容2000以上全部版本,有一个数据库名字是 LocalDB。固然你也能够修改为你实际的链接字符串。数据库
以后,咱们的查询示例,都将采用这个链接配置。安全
注意:最新版本的SOD框架,若是使用的是SqlServer,而且链接字符串指定了数据库名字但实际上没有这个数据库,框架能够自动建立数据库,此功能须要SOD的Code First功能支持,请参考下面“1.5 ,建立数据库表”的功能。框架
SOD框架最基本的配置,仅须要这一个地方,这比起EF来讲要简单。ide
若是是SqlServer +EF Code First方式的链接配置,SOD框架也可使用它这个链接字符串的。svn
咱们将使用最多见的用户登陆来举例,这里须要一个用户实体类,假设它的定义是下面这个样子的:
public class User : EntityBase { public User() { TableName="Tb_User"; IdentityName = "UserID"; PrimaryKeys.Add("UserID"); } public int ID { get { return getProperty<int>("UserID"); } set { setProperty("UserID", value); } } public string Name { get { return getProperty<string>("Name"); } set { setProperty("Name", value, 50); } } public string Pwd { get { return getProperty<string>("Pwd"); } set { setProperty("Pwd", value, 50); } } public DateTime RegistedDate { get { return getProperty<DateTime>("RegistedDate"); } set { setProperty("RegistedDate", value); } } }
在上面的定义中,构造函数内指明了 IdentityName = "UserID";这表示当前实体类对应的表有一个叫作 UserID的自增列,每当插入实体类后,能够经过该自增列对应的属性获取到新插入的自增ID的值。
另外,咱们发现每一个实体类的属性中,都是这样的写法:
public int ID { get { return getProperty<int>("UserID"); } set { setProperty("UserID", value); } }
属性内 getProperty ,setProperty 方法内的第一个参数,就是属性对应的字段名字。
因此,这样的属性,SOD框架称为“持久化属性”。
能够看到,SOD实体类仍是比较简单的,它没有使用特性来申明数据库信息,这意味着你能够在运行时修改实体类影射的主键,自增字段,表名称等数据库元数据,而且不须要反射,这些特性构成了SOD框架简单而强大的基础。
在项目中添加一个 LocalDbContext.cs 文件,文件中添加以下代码,以便检查表 Tb_User 是否存在,若是不存在,则自动建立一个:
/// <summary> /// 用来测试的本地SqlServer 数据库上下文类 /// </summary> public class LocalDbContext : DbContext { public LocalDbContext() : base("local") { //local 是链接字符串名字 } #region 父类抽象方法的实现 protected override bool CheckAllTableExists() { //建立用户表 CheckTableExists<User>(); return true; } #endregion }
本例中使用SqlServer 做为查询示例,程序会自动使用 SqlServerDbContext 密封类来进行内部协做处理,但这取决于你的AdoHelper实例类型,若是是Oracle,则内部使用的是OracleDbContext 密封类。
若是你不须要ORM的 Code First功能,这个 DbContext 实现类是不须要写的。
OK,基本准备就绪,如今Main 方法里面能够写下面的代码开始工做了:
LocalDbContext context = new LocalDbContext();//自动建立表
准备工做所有完成,运行这一句代码,以后查看SqlServer管理工具,发现表 Tb_User 已经建立了。
SOD框架的ORM功能跟一般的ORM框架不一样,SOD框架的实体类上并无数据查询和持久化的方法,因此SOD的实体类是“很是纯粹的”实体类,你能够把它看做是一个数据容器,或者用来看成DTO,ViewModel使用,有关这个话题更详细的阐述,请看这篇文章:《DataSet的灵活,实体类的方便,DTO的效率:SOD框架的数据容器,打造最适合DDD的ORM框架》。
在进行真正的数据查询以前,得先有数据,因此咱们先测试数据的增删改。
首先删除以前的测试数据,能够采用OQL进行批量数据删除:
int count = 0; //删除 测试数据----------------------------------------------------- User user = new User(); OQL deleteQ = OQL.From(user) .Delete() .Where(cmp => cmp.Comparer(user.ID, ">", 0)) //为了安全,不带Where条件是不会所有删除数据的 .END; count= EntityQuery<User>.ExecuteOql(deleteQ, context.CurrentDataBase);
这里将用户ID 大于0的数据所有删除了,框架内置了数据安全机制,不会随意删除所有数据,因此为了清除所有数据,采用了上面的方法。
方式1,采用DbContext 的Add 方法:
count = 0; User zhang_san = new User() { Name = "zhang san", Pwd = "123" }; count += context.Add<User>(zhang_san);//采用 DbContext 方式插入数据
方式2,采用OQL:
User li_si = new User() { Name = "li si", Pwd = "123" }; OQL insertQ= OQL.From(li_si) .Insert(li_si.Name);//仅仅插入用户名,不插入密码
OQL还需EntityQuery 对象配合,才能够执行,继续看下面的代码:
AdoHelper db = MyDB.GetDBHelperByConnectionName("local"); EntityQuery<User> userQuery = new EntityQuery<User>(db); count += userQuery.ExecuteOql(insertQ);
下面是结果图:
方式3,直接EntityQuery 插入实体类:
User zhang_yeye = new User() { Name = "zhang yeye", Pwd = "456" }; count += EntityQuery<User>.Instance.Insert(zhang_yeye);//采用泛型 EntityQuery 方式插入数据
方式1,采用 DbContext 方式更新数据
li_si.Pwd = "123123"; count = context.Update<User>(li_si);//采用 DbContext 方式更新数据
方式2,采用OQL方式更新指定的数据
li_si.Pwd = "123456"; OQL updateQ= OQL.From(li_si) .Update(li_si.Pwd) //仅仅更新密码 .END; count += EntityQuery<User>.Instance.ExecuteOql(updateQ);//采用OQL方式更新指定的数据
方式3,采用泛型 EntityQuery 方式修改数据
li_si.Pwd = "888888"; count += EntityQuery<User>.Instance.Update(li_si);//采用泛型 EntityQuery 方式修改数据
前面增删改数据完成了,如今有了测试数据,咱们能够来进行ORM的数据查询测试了,这里使用用户登陆的例子来测试,框架提供了6种数据查询方式。
假设前端直接传递了一个 User 实体类对象,中间设置了用户名和密码,如今有一个登陆方法使用该对象,该方法详细内容以下所示:
/// <summary> /// 使用用户对象来登陆,OQL最简单最多见的使用方式 /// </summary> /// <param name="user"></param> /// <returns></returns> public bool Login(User user) { OQL q = OQL.From(user) .Select() .Where(user.Name, user.Pwd) //以用户名和密码来验证登陆 .END; User dbUser =q.ToEntity<User>();//ToEntity,OQL扩展方法 return dbUser != null; //查询到用户实体类,表示登陆成功 }
这里咱们使用了SOD框架的ORM查询语言--OQL,它的结构很是相似于SQL,你能够认为OQL就是对象化的SQL语句。
OQL表达式采用 .From.....END 的语法,对象的链式方法调用,只要敲点出来的就是正确的,这样没有SQL基础的同窗也能够很快掌握该查询语法,也能立刻做数据开发。
注意:在本例中,使用了OQL的扩展方法,所以须要引用下面的名字空间:
using PWMIS.Core.Extensions;
若是不使用扩展方法,能够采用泛型EntityQuery 的方法,请看下面的示例。
/// <summary> /// 使用用户对象来登陆,可是使用 OQLCompare 对象的 EqualValue 相等比较方式 /// </summary> /// <param name="user"></param> /// <returns></returns> public bool Login1(User user) { OQL q = OQL.From(user) .Select(user.ID) //仅查询一个属性字段 ID .Where(cmp => cmp.EqualValue(user.Name) & cmp.EqualValue(user.Pwd)) .END; User dbUser = EntityQuery<User>.QueryObject(q); return dbUser != null; //查询到用户实体类,表示登陆成功 }
跟例1同样,这里也要求user 对象的Name和Pwd属性必须事先有值。本例没有使用OQL的扩展方法。
本例只是对例子1作了下改进,重点在于登陆方法的参数不是用户对象,而是名字和密码参数。
/// <summary> /// 使用用户名密码参数来登陆,采用 EntityQuery 泛型查询方法 /// </summary> /// <param name="name"></param> /// <param name="pwd"></param> /// <returns></returns> public bool Login2(string name, string pwd) { User user = new User() { Name = name, Pwd = pwd }; OQL q = OQL.From(user) .Select(user.ID) .Where(user.Name, user.Pwd) .END; User dbUser = EntityQuery<User>.QueryObject(q); return dbUser != null; //查询到用户实体类,表示登陆成功 }
/// <summary> /// 使用用户名密码参数来登陆,使用早期的实例化OQL对象的方式,并使用OQLConditon 对象为查询条件 /// </summary> /// <param name="name"></param> /// <param name="pwd"></param> /// <returns></returns> public bool Login3(string name, string pwd) { User user = new User(); OQL q = new OQL(user); q.Select(user.ID).Where(q.Condition.AND(user.Name, "=", name).AND(user.Pwd, "=", pwd)); User dbUser = EntityQuery<User>.QueryObject(q); return dbUser != null; //查询到用户实体类,表示登陆成功 }
这是OQL早期的条件查询方式,缺点是无法构造复杂的查询条件。
OQLCompare 的操做符重载能够简化比较条件,以下所示:
/// <summary> /// 使用用户名密码参数来登陆,并使用操做符重载的查询条件比较方式 /// </summary> /// <param name="name"></param> /// <param name="pwd"></param> /// <returns></returns> public bool Login4(string name, string pwd) { User user = new User(); OQL q = OQL.From(user) .Select() .Where( cmp => cmp.Property(user.Name) == name & cmp.Property(user.Pwd) == pwd ) .END; User dbUser = EntityQuery<User>.QueryObject(q); return dbUser != null; //查询到用户实体类,表示登陆成功 }
使用泛型OQL查询(GOQL),对于单实体类查询最简单的使用方式,缺点是不能进行“连表查询”,即多个实体类联合查询。
/// <summary> /// 使用用户名密码参数来登陆,使用泛型OQL查询(GOQL),对于单实体类查询最简单的使用方式。 /// </summary> /// <param name="name"></param> /// <param name="pwd"></param> /// <returns></returns> public bool Login5(string name, string pwd) { User dbUser = OQL.From<User>() .Select() .Where((cmp, user) => cmp.Property(user.Name) == name & cmp.Property(user.Pwd) == pwd ) .END .ToObject(); return dbUser != null; //查询到用户实体类,表示登陆成功 }
SOD实体类的“主键”字段是能够修改的,这样你能够随时修改它,就像实体类原本的主键同样,用它来填充数据,本例就是判断是否填充成功当前实体类来判断用户是否能够登陆。
/// <summary> /// 使用用户名密码参数来登陆,可是根据实体类的主键来填充实体类并判断是否成功。 /// </summary> /// <param name="name"></param> /// <param name="pwd"></param> /// <returns></returns> public bool Login6(string name, string pwd) { User user = new User(); user.PrimaryKeys.Clear(); user.PrimaryKeys.Add("Name"); user.PrimaryKeys.Add("Pwd"); user.Name = name; user.Pwd = pwd; bool result= EntityQuery<User>.Fill(user);//静态方法,使用默认的链接对象 return result; }
前面的例子都只是查询一条数据,若是须要查询多条数据也很简单,参见下面的例子,如何查询全部姓 zhang 的用户:
/// <summary> /// 模糊查询用户,返回用户列表,使用OQLCompare 委托 /// </summary> /// <param name="likeName">要匹配的用户名</param> /// <returns>用户列表</returns> public List<User> FuzzyQueryUser(string likeName) { User user = new User(); OQL q = OQL.From(user) .Select() .Where(cmp => cmp.Comparer(user.Name, "like", likeName+"%") ) .END; List<User> users = EntityQuery<User>.QueryList(q); return users; }
前端调用:
//查询列表 var users=service.FuzzyQueryUser("zhang"); Console.WriteLine("模糊查询姓 张 的用户,数量:{0}",users.Count );
因此,查询多条数据,仅须要使用泛型 EntityQuery对象的QueryList 方法便可。一样,框架也为你提供了OQL对象扩展方法来直接查询列表数据。
这里再也不举例,个人博客文章也屡次说明过,请参考下面这个系列文章:
SOD框架的整个设计都采用了“条条道路通罗马”的原则,即多模式解决方案,对数据的开发不论是SQL-MAP ,ORM,Data Control 哪一种模式均可以“异曲同工”,对于OQL,这个也获得了充分的体现,好比上面的用户登陆功能,使用了7 种方式来实现,实际上还有3中查询方式本篇文章没有作成说明;而对于实体类的增,删,改,分别又提供了DbContext,OQL,泛型EntityQuery 等多种方式。
因此,SOD框架的使用很是灵活,你能够根据你的偏好,习惯,环境,来灵活使用,并且也容易扩展,所以,相对于EF这样的ORM框架来,SOD框架的ORM功能没有任何束缚,它自由,灵活,并且轻量,容易扩展,但不妨碍它的强大,好比对于分表分库的查询,数据的批量更新插入修改,数据库锁的直接支持等这些“企业级”数据开发需求的支持。
本篇文章的源码,已经上传在了框架的开源项目 pwmis.codeplex.com 网站上,能够经过下面的地址查看完整的源码:
http://pwmis.codeplex.com/SourceControl/latest#SOD/Test/SampleORMTest/Program.cs
源码下载,能够在下面的地址查看:
http://pwmis.codeplex.com/releases/view/117822 简单ORM示例程序--新手必读
有关框架更详细而完整的入门指引,请参考下面这篇文章:
PDF.NET SOD 开源框架红包派送活动 && 新手快速入门指引
更多完整而详细的信息,请看框架官网地址:
框架已经彻底开源,参看这篇文章:
一年之计在于春,2015开篇:PDF.NET SOD Ver 5.1彻底开源
另外,网友 广州-四糸奈 在他的"惊鸿哥的港湾"写了一篇基础入门介绍文章《PDF.NET最初等入门》,推荐你们看看。
网友 广州-银古 写了一篇《SOD让你的旧代码焕发青春》,讲述了如何改造老式僵化项目的过程,推荐你们看看。
CodePlex online : http://pwmis.codeplex.com/SourceControl/latest
CodePlex SVN : https://pwmis.codeplex.com/svn
GitHub : https://github.com/znlgis/PDF.NET-SOD
OSChina : http://git.oschina.net/dxzyx/SOD