qouoww

质量管理+软件开发=聚焦管理软件的开发与应用

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

本系列教程发上去以后,有的朋友问我为什么不用Code First呢?个人认为,各种方法各有千秋,而且不管用哪种模式,最后达到的效果是一样的,实质上使用的基本方法是相通的,这就足够了.况且前面已经全文翻译了一本书,如果重复这本书的内容,就没多大意思了,毫无疑问,本文对于Code First模式的学习也是有指导作用的,理解了基本的机制,再进行其他方面深入知识的掌握就容易了,不是吗?我在这里写的内容都是通过不断的实践摸索总结出来的,可以说是很基础的东西,并不是想要炫耀什么,只是因为前面自已走的弯路太多,不想让像我这种刚入门的人再重复一些不必要的弯路而已...

书归正传,接上期博客文章,继续将数据查询的问题学习完毕.

5)单值选择查询:

在SQL数据查询里,经常会用到SELECT DISTINCT查询,用来搜索数据中的唯一值,注意不是第一个,而是重复数据中的一个.

比如想要查询Product表中ProductUnit(单位)的个数,就会用到单值选择查询.Linq提供了Distinct扩展方法,解决这一问题,如此例,代码应为:

 var result = from c in ctx.Product
                           select c.ProductUnitId;
              return result.Distinct().Count();

而使用方法语法则应为:

return ctx.Product.Select(c => c.ProductUnitId).Distinct().Count();

6)多级排序:

比如按照ProductName排序完成后,还要按ProductUnitName再次进行逆序排序,最后再按ProductBigTypeName来排序,这需要使用下面的查询语法代码(接前述GetProductDepositoryByPrice方法里的代码):

var result = from c in ctx.Product
                           join d in ctx.ProductSmallType on c.ProductSmallTypeId equals d.ProductSmallTypeId
                           join e in ctx.ProductBigType on d.ProductBigTypeId equals e.ProductBigTypeId
                           join f in ctx.ProductUnit on c.ProductUnitId equals f.ProductUnitId
                           where c.ProductBasePrice<=50 && c.ProductBasePrice >=49

                           orderby c.ProductName,f.ProductUnitName descending,e.ProductBigTypeName
                           select new ProductDepository
                           {
                               ProductName = c.ProductName,
                               ProductBigTypeName = e.ProductBigTypeName,
                               ProductSmallTypeName = d.ProductSmallTypeName,
                               ProductUntName = f.ProductUnitName

                           };
              return result.ToList();

使用方法语法(按前述GetProductByPrice方法里的代码),则应写作:

return ctx.Product.Include("ProductUnit").
                  Include("ProductSmallType").Include("ProductSmallType.ProductBigType")
                  .Where(c => (c.ProductBasePrice <= 50 && c.ProductBasePrice >= 49))
                  .OrderBy(c=>c.ProductName )
                  .ThenByDescending(c=>c.ProductUnit.ProductUnitName)
                  .ThenBy(c=>c.ProductSmallType.ProductBigType.ProductBigTypeName)
                  .ToList();

7)Top 10的实现

只选择前几条数据可以使用Take方法,比如Take(10)就表示取结果的前10条.可以直接在方法语法后面加上这个扩展方法,各位园友请自行试验,在此不再举例.

8)实现快速分页的关键方法:Skip方法,这一方法可以跳过指定的记录条数,返回剩余的记录,这可以应用于快速分页,因为跳过的记录不会进入内存,也不会占用服务器资源,这样就可以直接显示指定页面的数据了,Skip方法和Take方法协同使用,可以只调用所需要的当前页面数据,大大缩小的查询数据的区间,分页效率最高.这一方法使用比较简单,就不再举例.

9)返回结果中第一个匹配给定条件的数据,应该使用First或FirstOrDefault方法,在给定条件有数据返回时.两个方法类似,当给定条件无数据返回时,First方法返回Null,而FirstOrDefault方法返回为默认元素(如果是字符串,返回为"",如果是数字,返回0等等),所以注意对返回结果的处理,特别是对First方法返回值的处理,不要出现引用Null值的错误,否则会引发异常.通常的做法是在使用前对返回值是否为Null进行判断,要么就使用FirstOrDefault方法.

前面介绍了大部分常用的查询功能,有了这些查询方法,就可以在实际工作中施展拳脚了,大家多尝试多练习,就会体会到Linq查询的妙处.

6、删除数据

删除数据有两种情况,一种是对一对多关系中的“多”进行删除,这很容易,直接删除就是,比如Produc表;另一种是对一对多关系中的“一”进行删除,如果没有设置级联删除,将无法直接删除,除非已经将符合条件的“多”的部分全部删除了,才能正常删除与“多”相关的“一”,个人建议开启级联删除功能,这样在实际开发工作中事半功倍,否则在实际运行中出现删除不了的错误,也不好进行调试。在SQL Server Management Stuido里可以很容易地实现级联删除功能,见下图:

imageimage

级联设置完成后,就可以方便地进行删除操作了。这时候,我们就不用再顾虑是否能够正确删除的问题了,只要查询到了数据,就可以删除。

想要删除数据,首先要找到数据,这就要用到前面所学的查询语句及工具了。

在删除数据时要注意,如果查询得到的是单条数据,例如使用First或FirstOrDefault方法获取的数据,直接调用实体类的DeleteObject方法就可以了,然后再调用SaveChanges方法将所有更改进行更新。但是如果查询得到的是数据集合,就不能像SQL语句那样对数据进行批量删除操作了,这时需要使用foreach循环语句遍历所有数据,逐个调用DeleteObject方法。最后统一调用SaveChanges方法即可(从SaveChanges方法名就可以看到,这个方法是可以批量处理数据的)。

 

比如想要删除ProductSmallType表中ProductBigTypeName(需要导航得到,原表中没有这个属性)以9结尾的所有数据,方法如下:

1)首先在Business文件夹中新建一个类,命名为DeleteData.cs;

2)在类中添加一个方法DeleteProductSmallTypeByBigTypeName(),代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProductEFDemo.Models;

namespace ProductEFDemo.Business
{
  public static  class DeleteData
    {
      public static void DeleteProductSmallTypeByBigTypeName()
      {
          using (var ctx = new ProductsEntities())
          {
              var result = ctx.ProductSmallType.Include("ProductBigType")
                         .Where(c => c.ProductBigType.ProductBigTypeName.EndsWith("9"))
                         .ToList();
              foreach (var productSmallType in result)
              {
                  ctx.ProductSmallType.DeleteObject(productSmallType);
              }
              ctx.SaveChanges();
          }
      }
    }
}

3)在Presenter文件夹中,添加一个类:DeleteObjectView.cs

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProductEFDemo.Business;

namespace ProductEFDemo.Presenter
{
  public static  class DeleteObjectView
    {
      public static void DeleteProductSmallTypeByBigTypeName()
      {
          Console.WriteLine("真的要全部删除?");
          string str = Console.ReadLine();
          if (str.Substring(0, 1).ToLower() == "y")
          {
              try
              {
                  DeleteData.DeleteProductSmallTypeByBigTypeName();
                  Console.WriteLine("成功删除所有相关数据及相关关联数据");
              }
              catch (Exception ex)
              {

                  Console.WriteLine(String.Format ("出错,错误信息为:{0}",ex.Message));
              }
           

          }
          
      }
    }
}

4)在Programm.cs中的Main函数调用这一方法,结果如下:

image

再到数据库的ProductSmallType表里去找相关数据,已经没有了,记录已经变成了90条,到Product表里去找相关数据,记录变成了9000条,有1000条数据也被同时删除了,尽管我们并没有显示地指定要删除这些数据,但是由于设置了级联,就将一对多中的多的部分也删除了。

当然了,这种删除是对数据的永久性删除,一般来说不是一个很好的模式,万一想反悔已经来不及了。所以大型项目通常都是设置一个数据列,标记被删除的那一行数据为已删除(实际上数据还在,只是不在前台里显示了而已),这需要使用更新语句,而不是真正的删除语句,然后以后在确定不会后悔了以后再真的将数据清空,建议园友们在实际项目中也这样操作,算是一味后悔药吧!

 

7、更新和修改数据

这是本系列的最后一部分,实现了添加,查找,删除,下面就该实现数据的修改了。数据修改的方法与数据删除的方法相类似,也是先要通过Linq查询到数据,然后给查询到数据对象的相关属性进行赋值修改,最后再调用SaveChanges方法更新就OK了。

同样,对于集合类数据的修改,也不能一并进行更新处理,只能通过循环对各个对象逐个进行更新处理。这一点一定要注意!

下面对ProductUnit表中的ProductName进行修改,将所有“测试”字符串替换为””,步骤如下:

1)首先还是在Business文件夹中添加一个类:UpdateData.cs:

2)然后在类中添加一个方法实现前面要实现的更新功能:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProductEFDemo.Models;

namespace ProductEFDemo.Business
{
  public static  class UpdateData
    {
      public static void UpdateProductUnitByUnitName()
      {
          using (var ctx = new ProductsEntities())
          {
              var result = ctx.ProductUnit.Where(c => c.ProductUnitName.Contains("测试")).ToList();
              foreach (var productUnit in result)
              {
                  productUnit.ProductUnitName = productUnit.ProductUnitName.Replace("测试", "");
              }
              ctx.SaveChanges();
          }
      }
    }
}

 

3)在Presenter文件夹中添加一个类:UpdateDateView.cs。

4)在类中实现一个方法调用刚刚创建的方法:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProductEFDemo.Business;

namespace ProductEFDemo.Presenter
{
  public static class UpdateDateView
    {
      public static void UpdateProductUnitByUnitName()
      {
         try
         {
             Console.WriteLine("准备更新数据...");
             UpdateData.UpdateProductUnitByUnitName();
             Console.WriteLine("更新成功!");
         }
         catch (System.Exception ex)
         {
             Console.WriteLine(String.Format("出错,错误信息为:{0}", ex.Message));
         }
         
      }
    }
}

5)执行程序,结果如下:

image

到数据库中检索数据,

image

可见数据都进行了更新和修改。是不是很方便?

总结一下,想怎样改数据都是在实体类的对象上改,对象改完以后直接SaveChanges就OK了,很简单很方便!

 

8、小结:Entity Framework还有很多高级知识,如延迟加载(Lazy Load),数据库自定义加载,并发处置等,这些还需要进一步研究和讨论,但是掌握了基础知识以后,这些新的知识就可以获得更好的理解了,希望园友们再接再厉,深入研究EF这一ORM工具,大幅度地提高生产力!

 

声明:本文系本人原创,版权归属作者和博客园共同所有,任何组织或个人不得随意转载,修改。需要转载请与本人联系:qouoww@163.com。

posted on 2012-04-27 17:20  qouoww  阅读(4836)  评论(10编辑  收藏  举报