zoukankan      html  css  js  c++  java
  • 一步一步学Entity Framework 4(2)

    上一期博客末尾给出了一个静态类,四个静态方法。这有什么用呢?在继续进行其他内容前,我们先利用这些静态方法为数据库生成基础数据。按照ADO.NET的常规方法,需要先建立连接,创建DataSet或DataReader对象,构造SQL语句,然后执行SQLCommand命令进行作业。现在有了EF,这些步骤全部省掉,EF的内部已经自动实现了这些步骤,我们所要做的只是去调用刚刚定义的几个方法。

    在项目上新建一个文件夹,命名为Presenter,用来包含一些与用户交互操作的方法,以便Main函数进行调用。在Presenter文件中下新建一个类,命名为AddDataView.cs,该类就包含一个方法,用以向数据内插入数据:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using ProductEFDemo.Business;
    
    namespace ProductEFDemo.Presenter
    {
       public static class AddDataView
        {
           public static void AddAllData()
           {
               
               try
               {
                   //添加单位数据
                   InsertData.AddProudctUnit();
                   Console.WriteLine("产品单位测试数据插入成功!");
                   //添加产品大类数据
                   InsertData.AddProductBigType();
                   Console.WriteLine("产品大类测试数据插入成功!");
    
                   //添加产品小类数据
                   foreach (var productBigType in SearchData.GetAllProductBigType())
                   {
                       InsertData.AddProductSmallType(productBigType.ProductBigTypeId);
                   }
                   Console.WriteLine("产品小类测试数据插入成功!");
    
                   //添加产品数据
                   foreach (var productSmallType in SearchData.GetAllProductSmallType())
                   {
                       foreach (var productUnit in SearchData.GetAllProductUnit())
                       {
                           InsertData.AddProduct(productUnit.ProductUnitId, productSmallType.ProductSmallTypeId);
                       }
                   }
                   Console.WriteLine("添加产品测试数据成功!");
    
    
               }
               catch (System.Exception ex)
               {
                   Console.WriteLine(String.Format("发生错误,错误信息为:{0}",ex.Message) ;              
               }
           }
        }
    }
    

    该类中调用的三个方法:GetAllProductBigType(),GetAllProductUnit(),GetAllProductSmallType()代码如下,由于这些是查询的内容,相关信息后面再详细介绍。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using ProductEFDemo.Models;
    
    namespace ProductEFDemo.Business
    {
      public static  class SearchData
        {
          public static List<ProductBigType> GetAllProductBigType()
          {
              using (var ctx = new ProductsEntities())
              {
                  return ctx.ProductBigType.ToList();
              }
          }
    
          public static List<ProductUnit> GetAllProductUnit()
          {
              using (var ctx = new ProductsEntities())
              {
                  return ctx.ProductUnit.ToList();
              }
    
          }
    
          public static List<ProductSmallType> GetAllProductSmallType()
          {
              using (var ctx = new ProductsEntities())
              {
                  return ctx.ProductSmallType.ToList();
              }
          }
        }
    }
    

    代码没有什么可介绍的,注意有关命名空间的引用。

    现在开始调用,在项目的Program.cs的Main方法里调用刚刚定义的AddAllData方法:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using ProductEFDemo.Presenter;
    
    namespace ProductEFDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                AddDataView.AddAllData();
                Console.ReadKey();
            }
        }
    }
    

    注意引用ProductEFDemo.Presenter命名空间。

    生成并运行程序,

    image

    打开SqlSever Management Studio,展开表,发现数据已经全部插入了:

    image

    你猜猜使用上述代码一共生成了多少条Product数据?到目前为止,一句SQL代码都没有写,写的代码都是纯粹的C#代码,而且还有额外的IDE代码提示,是不是很爽?强烈建议不要拷贝代码,而是一行行输入,这样才能感受到书写代码的快乐。

    5、数据查询

    现在有了数据,就可以随心所欲地进行查询了。这里先对前面调用的GetAllProductBigType(),GetAllProductUnit(),GetAllProductSmallType()作一个解释:

    前面三个方法是获取集合性质数据的典型方法,使用的是ToList()扩展方法(关于扩展方法请自已去MSDN查去,我觉得内置的扩展方法就够用了,暂时还不想去自定义!),就可以将实体类的数据进行序列化,成为List范型类,List范型类很好用,可以直接绑定为很多集合类控件的数据源,不管你是用WinForm,还是.Net MVC或是WPF,SIlverlight等,都会广泛地用到,但这超出了我想表达内容的范围,就不赘述了。下面就Linq查询作一介绍。原来我对Linq很抵触,在《C#入门经典》读到这一章的时候,我选择的是跳过,不去看它。后来有时间再翻阅书的时候,惊讶地发现我错过了多么美丽的风景!太简洁,太优雅了!真心感谢微软,推出了这么好用的工具和语法。。。好了,不吹捧微软了,我们还是先来了解一下Linq的基本知识:

    所有的的Linq都是引用自System.Linq名称空间的类,因此在使用前要检查是否引用该名称空间,实际上VS在创建类的模板里已经默认添加了这个引用,如果没有请自行添加。

    Linq用于查询数据的语法有两种,一种称为查询语法,有点类似于Sql语句,与Sql语句不同的地方是from在前,select在后,from,select,where,distinct,join(注意全部为小写)现在已经成为C#的关键字,专门用于Linq查询,这种方法一般是首选方法,因为它一般更容易理解,但是可惜的是,Linq的全部功能不能由查询语法实现(比如前面的ToList方法在LInq里没有对应关键字),需要借助于另一种语法,称为方法语法,相比而言,方法语法使用起来稍微难理解一点,但是方法语法的优点是比较简洁,代码更优雅。下面两种方法都介绍,可以先用查询语法,熟练以后可以试试方法语法,其实也挺好的!

    1)简单查询:场景是这样的,需要从ProductBigType类中查询出包含数字“7”的记录,可以这样写:

    using(var ctx=new ProductsEntities())
    {
        var result=from c in ctx.ProductBigType
                             where c.ProductBigTypeName.Contains("7")
                             select c;
                  return result.ToList();
    }
    如果采用方法语法则可以简化为(为代码简洁起见,后面不再引用using字句):
    return ctx.ProductBigType.Where(c => c.ProductBigTypeName.Contains("7")).ToList();

    返回的记录是一样的,只不过方法更优雅;

    2)数据排序

    查询语法使用orderby关键字,如果采用降序,可以添加descending关键字;下面示例的场景是这样的,需要从查询出的所有的ProductBigType中查询出首个单词为“产品”的记录(其实就是全部记录),按降序排序,代码如下:

    查询语法:

    var result=from c in ctx.ProductBigType 
                             where c.ProductBigTypeName .StartsWith("产品")
                             orderby c.ProductBigTypeName descending
                             select c;
                  return result.ToList();
    方法语法:
    return ctx.ProductBigType.Where(c => c.ProductBigTypeName.StartsWith("产品")).OrderByDescending(c => c.ProductBigTypeName).ToList();

    方法语法这种长串的语法称为链式语句,意思像一条长链一样,这是因为所用的方法是扩展方法,都具有相同的类型(实现了IQueryable或IOrderable范型接口),所以可以连接起来;

    3)聚合运算符:用于统计数据,与SQL语句中的相关聚合运算符类似,不同之处这些运算符都是扩展方法,首字母必须大写,常用的聚合扩展方法如下:

    Count(),表示结果的个数;

    Min(),表示结果中的最小值

    Max(),表示最大值;

    Average(),表示数据结果的平均值;

    Sum(),表示求和;

    现在我们想要查询出Product表中ProductBaseValue中最大的数据,就可以这样写:

    public decimal MaxProductBasePrice()
          {
              using (var ctx = new ProductsEntities())
              {
                  var result= from c in ctx.Product
                              select c.ProductBasePrice;
                  return result.Max();
              }
          }
    或者采用方法语法:
    return ctx.Product.Max(c => c.ProductBasePrice);

    4)使用导航属性:

    昨天博客发表以后,有园友问为什么要用Entity Framework4.x POCO Generator,除了节省力气以外,很重要的一个原因是自已手工写的类经常会忘记导航属性,导致在使用Linq无法导航到所需要的数据,不得不借助join来完成。实际上,Sql的内联Join关键字完全可以少用或者不用(虽然Linq同样提供了jion关键字和Join扩展方法),而采用一个新扩展方法Include来解决内联的问题,可以顺利地完成导航。在使用导航属性进行内联接之前,我们先来看看POCO生成的类:

        public partial class Product
        {
            #region Primitive Properties
        
            public virtual System.Guid ProductId
            {
                get;
                set;
            }
        
            public virtual string ProductName
            {
                get;
                set;
            }
        
            public virtual System.Guid ProductSmallTypeId
            {
                get { return _productSmallTypeId; }
                set
                {
                    if (_productSmallTypeId != value)
                    {
                        if (ProductSmallType != null && ProductSmallType.ProductSmallTypeId != value)
                        {
                            ProductSmallType = null;
                        }
                        _productSmallTypeId = value;
                    }
                }
            }
            private System.Guid _productSmallTypeId;
        
            public virtual System.Guid ProductUnitId
            {
                get { return _productUnitId; }
                set
                {
                    if (_productUnitId != value)
                    {
                        if (ProductUnit != null && ProductUnit.ProductUnitId != value)
                        {
                            ProductUnit = null;
                        }
                        _productUnitId = value;
                    }
                }
            }
            private System.Guid _productUnitId;
        
            public virtual decimal ProductBasePrice
            {
                get;
                set;
            }
        
            public virtual decimal ProductLosePrice
            {
                get;
                set;
            }
    
            #endregion
            #region Navigation Properties
        
            public virtual ProductSmallType ProductSmallType
            {
                get { return _productSmallType; }
                set
                {
                    if (!ReferenceEquals(_productSmallType, value))
                    {
                        var previousValue = _productSmallType;
                        _productSmallType = value;
                        FixupProductSmallType(previousValue);
                    }
                }
            }
            private ProductSmallType _productSmallType;
        
            public virtual ProductUnit ProductUnit
            {
                get { return _productUnit; }
                set
                {
                    if (!ReferenceEquals(_productUnit, value))
                    {
                        var previousValue = _productUnit;
                        _productUnit = value;
                        FixupProductUnit(previousValue);
                    }
                }
            }
            private ProductUnit _productUnit;
    
            #endregion
            #region Association Fixup
        
            private void FixupProductSmallType(ProductSmallType previousValue)
            {
                if (previousValue != null && previousValue.Product.Contains(this))
                {
                    previousValue.Product.Remove(this);
                }
        
                if (ProductSmallType != null)
                {
                    if (!ProductSmallType.Product.Contains(this))
                    {
                        ProductSmallType.Product.Add(this);
                    }
                    if (ProductSmallTypeId != ProductSmallType.ProductSmallTypeId)
                    {
                        ProductSmallTypeId = ProductSmallType.ProductSmallTypeId;
                    }
                }
            }
        
            private void FixupProductUnit(ProductUnit previousValue)
            {
                if (previousValue != null && previousValue.Product.Contains(this))
                {
                    previousValue.Product.Remove(this);
                }
        
                if (ProductUnit != null)
                {
                    if (!ProductUnit.Product.Contains(this))
                    {
                        ProductUnit.Product.Add(this);
                    }
                    if (ProductUnitId != ProductUnit.ProductUnitId)
                    {
                        ProductUnitId = ProductUnit.ProductUnitId;
                    }
                }
            }
    
            #endregion
        }

    其中ProductUnit和ProductSmallType两个属性就是导航属性,这两个属性就可以在实际使用中进行内连接;

    比如我们想在控制台中输出若干条Product记录(这里的搜索条件为ProductBasePrice介于49-50之间),要求显示格式为:产品名称:XXXX;产品大类:XXX;产品小类:XXX;单位:XXX;基本价格:XXX;

    就需要通过内联从其他表中获取数据,就要用到前面所说的导航属性。注意到Product表只导航到了ProductSmallType,还需要借助ProductSmallType类导航到ProductBigType。(如果您不明白,还请再回头看有关数据库表的说明)。

    这样代码就可以这样写:

    public static List<Product> GetProductByPrice()
          {
              using (var ctx = new ProductsEntities())
              {
                  return ctx.Product.Include("ProductUnit").
                      Include("ProductSmallType").Include("ProductSmallType.ProductBigType")
                      .Where(c => (c.ProductBasePrice <= 50 && c.ProductBasePrice >= 49)).ToList();
      
              }
          }

    为了实现前面所说的输出,需要在Presenter文件夹中新建一个类,类中有一个方法对相关数据进行处理:

     public static class ProductDeatilView
        {
           public static void ShowProductDeatil()
           {
               foreach (var product in SearchData.GetProductByPrice())
               {
                   Console.WriteLine(String.Format("产品名称:{0};产品大类:{1};产品小类:{2};单位:{3};基本价格:{4};",
                       product.ProductName, product.ProductSmallType.ProductBigType.ProductBigTypeName,
                       product.ProductSmallType.ProductSmallTypeName, product.ProductUnit.ProductUnitName, product.ProductBasePrice));
               }
           }
        }

    ShowProductDeatile方法只有一个语句,在调用时就使用了Product类的导航属性,只要你想,可以导航到任何数据,但前提是已经将数据都包容进来了。

    现在在Main方法中调用ShowProductDeatile方法,输出结果如下:

    image

    这个例子使用查询语法也简单不了多少,因为并没有include关键字可用,也必须要使用Include扩展方法。在此就不举例了。需要注意的是关于Include扩展方法的参数问题,这个参数是导航的路径,必须是一个字母不错的导航属性名,否则会因为查询不到数据而引发异常。如果需要通过导航属性再导航到其他相关的导航属性上,则需要使用点式语法,如上例中对ProductBigType导航属性的引用,就需要使用Include("ProductSmallType.ProductBigType")这样的语法;你也可以试试去掉某一个Include语句,试试会发生什么问题。

    这个例子还有一种相对麻烦的实现方法,就是新建一个ProductDepository类,将需要展示出的字段放进去,然后使用Linq的join关键字来进行查询,新建的ProductDepository类:

    public  class ProductDepository
        {
            public string ProductName { get; set; }
            public string ProductBigTypeName { get; set; }
            public string ProductSmallTypeName { get; set; }
            public string ProductUntName { get; set; }
            public decimal ProductBasePrice { get; set; }
        }

    而查询语句如下:

     public static List<ProductDepository> GetProductDepositoryByPrice()
          {
              using (var ctx = new ProductsEntities())
              {
                  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
                               select new ProductDepository
                               {
                                   ProductName = c.ProductName,
                                   ProductBigTypeName = e.ProductBigTypeName,
                                   ProductSmallTypeName = d.ProductSmallTypeName,
                                   ProductUntName = f.ProductUnitName
    
                               };
                  return result.ToList();
              }
          }

    调用数据的方法则变为:

     public static void ShowProductDeatil()
           {
               foreach (var productDepository in SearchData.GetProductDepositoryByPrice())
               {
                           Console.WriteLine(String.Format("产品名称:{0};产品大类:{1};产品小类:{2};单位:{3};基本价格:{4};",
                       productDepository.ProductName, productDepository.ProductBigTypeName,
                       productDepository.ProductSmallTypeName, productDepository.ProductUntName,
                       productDepository.ProductBasePrice));
                       
               }
           }

    输出的效果是一样的。这种方法虽然比较麻烦,但是当类的关第比较复杂,需要重新梳理时,这不失为一种有效的解决方案,由于类可以无限制扩展,在进行类似WPF或SilverLIght的MVVM模式开发中,可以很方便地将模型与类本身进行分离。但是一般来说,使用导航属性方便的时候,大可不必这么大动干戈,具体怎样取舍,取决于程序的复杂程度。

    另外,查询语法的select字句不支持多个字段的语法,因此要想使用多个字段,就必须在查询中创建新对象,可以显示地创建类,也可以创建无名称的对象,术语将这种情况称为投影(projection);使用方法语法也可以进行实现投影,代码如下:

    var result=product.Select(c=>new{c.ProductName,c.ProductBasePrice});

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

  • 相关阅读:
    Asp.Net Mvc: 应用BindAttribute
    Mvc内建功能(DefaultModelBinder)自动绑定。
    生成随机字母字符串(数字字母混和)
    C#中实现输入汉字获取其拼音(汉字转拼音)的2种方法
    集合里查找数据
    C#自定义导出数据到Excel中的类封装
    MySQL性能优化的最佳20+条经验
    DevExpress.XtraGrid.view.gridview 属性说明
    c# 连接Mysql数据库
    ADO.NET 结构 集中数据库联接结构
  • 原文地址:https://www.cnblogs.com/qouoww/p/2472967.html
Copyright © 2011-2022 走看看