zoukankan      html  css  js  c++  java
  • .NET ORM 的 “SOD蜜”--零基础入门篇

    PDF.NET SOD框架不仅仅是一个ORM,但是它的ORM功能是独具特色的,我在博客中已经多次介绍,但都是原理性的,可能不少初学的朋友还是觉得复杂,其实,SOD的ORM是很简单的。下面我们就采用流行的 Code First的方式,一步步来了解下。

    注意:也可以参考笔者撰写的新书:《SOD框架企业级应用数据架构实战》 来了解本篇文章的内容。

    一、准备工作

    1.1,添加SOD包引用

    首先建立一个控制台项目(支持.NET2.0的项目),并使用程序包管理器添加PDF.NET SOD的程序引用:

    PM> Install-Package PDF.NET.SOD

    更多详细使用信息说明,请参考nuget 网站说明 https://www.nuget.org/packages/PDF.NET/

    1.2,配置数据连接

    新建一个控制台项目,添加一个应用程序配置文件,增加一个数据库连接配置:

    <?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来说要简单。

    如果是SqlServer +EF Code First方式的连接配置,SOD框架也可以使用它这个连接字符串的。

    1.3,定义实体类

    我们将使用最常见的用户登录来举例,这里需要一个用户实体类,假设它的定义是下面这个样子的:

    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); }
            }
    
        }
    View Code

    在上面的定义中,构造函数内指明了 IdentityName = "UserID";这表示当前实体类对应的表有一个叫做 UserID的自增列,每当插入实体类后,可以通过该自增列对应的属性获取到新插入的自增ID的值。

    另外,我们发现每个实体类的属性中,都是这样的写法:

        public int ID
            {
                get { return getProperty<int>("UserID"); }
                set { setProperty("UserID", value); }
            }

    属性内 getProperty ,setProperty 方法内的第一个参数,就是属性对应的字段名字。
    所以,这样的属性,SOD框架称为“持久化属性”。

    可以看到,SOD实体类还是比较简单的,它没有使用特性来申明数据库信息,这意味着你可以在运行时修改实体类影射的主键,自增字段,表名称等数据库元数据,并且不需要反射,这些特性构成了SOD框架简单而强大的基础。

    1.4,添加查询对象的数据上下文

    在项目中添加一个 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 实现类是不需要写的。

    1.5,创建数据库表

    OK,基本准备就绪,现在Main 方法里面可以写下面的代码开始工作了:

    LocalDbContext context = new LocalDbContext();//自动创建表

    准备工作全部完成,运行这一句代码,之后查看SqlServer管理工具,发现表 Tb_User 已经创建了。

    二、ORM之增,删,改

    SOD框架的ORM功能跟通常的ORM框架不同,SOD框架的实体类上并没有数据查询和持久化的方法,所以SOD的实体类是“非常纯粹的”实体类,你可以把它看作是一个数据容器,或者用来当作DTO,ViewModel使用,有关这个话题更详细的阐述,请看这篇文章:《DataSet的灵活,实体类的方便,DTO的效率:SOD框架的数据容器,打造最适合DDD的ORM框架》。

    在进行真正的数据查询之前,得先有数据,所以我们先测试数据的增删改。

    2.1,删除数据

    首先删除之前的测试数据,可以采用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的数据全部删除了,框架内置了数据安全机制,不会随意删除全部数据,所以为了清除全部数据,采用了上面的方法。

    2.2,增加数据

    方式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 方式插入数据

    2.3,修改数据:

    方式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之数据查询

    前面增删改数据完成了,现在有了测试数据,我们可以来进行ORM的数据查询测试了,这里使用用户登录的例子来测试,框架提供了6种数据查询方式。

     3.1,最简单的方法

    假设前端直接传递了一个 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 的方法,请看下面的示例。

    3.2,使用 OQLCompare 对象的 EqualValue 相等比较方式

            /// <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的扩展方法。

    3.3, EntityQuery 泛型查询方法

    本例只是对例子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; //查询到用户实体类,表示登录成功
            }

    3.4,使用OQLConditon 对象为查询条件

           /// <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早期的条件查询方式,缺点是没法构造复杂的查询条件。

     3.5,操作符重载

    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; //查询到用户实体类,表示登录成功
            }

     3.6,使用泛型OQL查询(GOQL)

    使用泛型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; //查询到用户实体类,表示登录成功
            }


    3.7,使用实体类主键来查询

    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;
            }


    3.8,查询多条数据

    前面的例子都只是查询一条数据,如果需要查询多条数据也很简单,参见下面的例子,如何查询所有姓 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对象扩展方法来直接查询列表数据。

    3.9,实体类联合查询

    这里不再举例,我的博客文章也多次说明过,请参考下面这个系列文章:

    ORM查询语言(OQL)简介--高级篇(续):庐山真貌 深蓝医生 2013-07-30 16:54 阅读:4497 评论:41  
     
    ORM查询语言(OQL)简介--高级篇:脱胎换骨 深蓝医生 2013-07-26 17:26 阅读:3274 评论:28  
     
    ORM查询语言(OQL)简介--实例篇 深蓝医生 2013-04-01 14:56 阅读:5108 评论:16  
     
    ORM查询语言(OQL)简介--概念篇 深蓝医生 2012-10-06 00:58 阅读:4657 评论:25  
     

    四、SOD框架的设计原则

     SOD框架的整个设计都采用了“条条道路通罗马”的原则,即多模式解决方案,对数据的开发不管是SQL-MAP ,ORM,Data Control 哪种模式都可以“殊途同归”,对于OQL,这个也得到了充分的体现,比如上面的用户登录功能,使用了7 种方式来实现,实际上还有3中查询方式本篇文章没有做成说明;而对于实体类的增,删,改,分别又提供了DbContext,OQL,泛型EntityQuery 等多种方式。

    所以,SOD框架的使用非常灵活,你可以根据你的偏好,习惯,环境,来灵活使用,而且也容易扩展,因此,相对于EF这样的ORM框架来,SOD框架的ORM功能没有任何束缚,它自由,灵活,而且轻量,容易扩展,但不妨碍它的强大,比如对于分表分库的查询,数据的批量更新插入修改,数据库锁的直接支持等这些“企业级”数据开发需求的支持。

    五、相关资源

    本篇文章的源码,已经在框架的源码里面。

    有关框架更详细而完整的入门指引,请参考下面这篇文章:

    PDF.NET SOD 开源框架红包派送活动 && 新手快速入门指引

    更多完整而详细的信息,请看框架官网地址:

    http://www.pwmis.com/sqlmap

    或者参考笔者撰写的新书:《SOD框架企业级应用数据架构实战

    框架已经完全开源,参看这篇文章:

    一年之计在于春,2015开篇:PDF.NET SOD Ver 5.1完全开源

    另外,  网友 广州-银古 写了一篇《SOD让你的旧代码焕发青春》,讲述了如何改造老式僵化项目的过程,推荐大家看看。

    六、最新源码资源地址


    CodePlex online : http://pwmis.codeplex.com/SourceControl/latest [注意:已经过时]

    CodePlex SVN : https://pwmis.codeplex.com/svn [注意:已经过时]

    GitHub : https://github.com/znlgis/SOD

    OSChina : https://gitee.com/znlgis/sod

  • 相关阅读:
    MOSS中的User的Title, LoginName, DisplayName, SID之间的关系
    如何在Network Monitor中高亮间隔时间过长的帧?
    SharePoint服务器如果需要安装杀毒软件, 需要注意什么?
    如何查看SQL Profiler? 如何查看SQL死锁?
    什么是Telnet
    The name or security ID (SID) of the domain specified is inconsistent with the trust information for that domain.
    Windows SharePoint Service 3.0的某个Web Application无搜索结果
    网络连接不上, 有TCP错误, 如果操作系统是Windows Server 2003, 请尝试一下这里
    在WinDBG中查看内存的命令
    The virtual machine could not be started because the hypervisor is not running
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/4306131.html
Copyright © 2011-2022 走看看