ASP.NET-Entity Framework学习
学习过程来源bilibili,附链接:Entity Framework实践
文章目录
一、委托
1.委托的概述与声明
- 委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。
- 可以通过委托实例调用方法。也可以使用委托将方法作为参数传递给其他方法。
- 委托的使用将大大提高程序的可扩展性。
- 委托的声明决定了可由该委托引用的方法。委托可指向一个与其具有相同签名的方法:
声明语法
public delegate <返回值类型> <委托名> <参数列表>
示例
public delegate string TestDelegate(string s)
这里就是声明了一个string类型,带有一个参数的委托类型TestDelegate
能被委托调用的方法要满足条件:
- 方法的返回值类型必须和委托定义的一致。
- 方法的参数类型、个数和顺序必须和委托定义的一致。
- 大部分时候被委托执行的方法是static类型的
下面是一个示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
//定义一个委托类型
public delegate string Dele(string name);
static void Main(string[] args)
{
//实例化委托方法1,new时必须指定委托方法名
Dele d1 = new Dele(Show1);
//实例化委托方法2,直接赋值需要委托的方法名
Dele d2 = Show1;
//通过委托执行方法,方法1
d1("张三");
//通过委托执行方法,方法2
d2.Invoke("王五");
}
//测试方法1
public static string Show1(string name)
{
Console.WriteLine("你好,{0}",name);
return name;
}
}
}
运行结果:
上面示例代码的深入一
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
//定义一个委托类型
public delegate int Dele(int x);
static void Main(string[] args)
{
//实例化委托
Dele d = Cacl;
//通过委托执行方法
int result1=d(2);
//修改委托绑定的方法
d = CaclA;
//通过委托执行方法,方法2
int result2 = d(2);
Console.WriteLine("{0},{1}", result1, result2);
}
//测试方法1
public static int Cacl(int x)
{
return x*x;
}
//测试方法2,箭头函数,等效于Cacl方法
public static int CaclA(int x) => x * x*x;
}
}
运行结果:
上面的示例代码深入二
将委托作为参数传递,在方法中调用委托方法。using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
//定义一个委托类型,此处放在类外面作为全局类型
public delegate int Dele(int x);
class Program
{
static void Main(string[] args)
{
int[] v = { 1, 2, 3 };
//实例化委托,并委托方法
Dele d = Square;
//d=Cube;//修改委托的方法,达到不修改源程序,让数组结果变化的目的
Util.Show(v, d);
//打印数组的值
for (int i = 0; i < v.Length; i++)
{
Console.WriteLine(v[i]);
}
}
//计算平方的方法
public static int Square(int x) => x * x;
//计算立方的方法
public static int Cube(int x) => x * x * x;
}
class Util
{
/// <summary>
/// 一个带委托参数的方法
/// </summary>
/// <param name="values">数组</param>
/// <param name="d">委托参数</param>
public static void Show(int[] values,Dele d)
{
for(int i=0;i<values.Length;i++)
{
//修改每一个元素的值,通过调用委托方法
values[i] = d(values[i]);
}
}
}
}
运行结果:
2.多播委托
所有的委托实例都具有多播的能力,一个实例可以引用多个目标方法。
注意: 委托的调用顺序和它们定义的顺序是一致的,也就是说先执行第一个委托的方法,紧接着依次执行委托的方法。
示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
//定义一个委托类型,此处放在类外面作为全局类型
public delegate int Dele(int x);
class Program
{
static void Main(string[] args)
{
//实例化委托,并委托第一个方法
Dele d = Square;
//委托第二个方法
d += Cube;
//执行委托
d(2);
}
//计算平方的方法
public static int Square(int x)
{
Console.WriteLine("平方方法被执行");
return x * x;
}
//计算立方的方法
public static int Cube(int x)
{
Console.WriteLine("立方方法被执行");
return x * x * x;
}
}
}
运行结果:
注意: 多播委托调用的方法的返回值是最后一个委托方法的返回值,可以自行输出上面示例代码委托方法的返回值。
案例:利用委托,设计方法method1实现计算长方形的周长,method2实现计算长方形的面积,通过委托两个方法同时计算出周长和面积。
示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
//定义一个委托类型,此处放在类外面作为全局类型
public delegate void Dele(int w,int h);
class Program
{
static void Main(string[] args)
{
//实例化委托,并委托计算周长的方法和计算面积的方法
Dele d = Method1;
d += Method2;
//执行委托
d(3,4);
}
/// <summary>
/// 计算周长
/// </summary>
/// <param name="w">宽</param>
/// <param name="h">高</param>
/// <param name="perimeter">周长,引用参数</param>
public static void Method1(int w,int h)
{
Console.WriteLine("周长:{0}", (w + h) * 2);
}
/// <summary>
/// 计算面积
/// </summary>
/// <param name="w">宽</param>
/// <param name="h">高</param>
public static void Method2(int w, int h)
{
Console.WriteLine("面积:{0}", w * h);
}
}
}
运行结果:
二、匿名方法
匿名方法顾名思义,就是没有名字的方法。
匿名方法 提供了一种传递代码块作为委托参数==的技术,匿名方法是没有名称只有主体的方法。在匿名方法中不需要指定返回类型,它是从方法主体内的return语句推断的。
匿名方法既然没有名字,也就是说在别的地方没有办法通过名字来调用这个方法,所以匿名方法一般只用一次。创建匿名方法的前提就是你要把它放到一个委托里面。
注意:匿名方法的返回值由return的结果自行推断。
匿名方法的语法结构:
- 先定义并实例化一个委托
public delegate int Dele(int x, int y);
- 定义匿名方法
delegate(形参列表){ return 返回值; }
下面展示一个委托的匿名方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
//定义一个委托
public delegate int Dele(int x, int y);
class Program
{
static void Main(string[] args)
{
//委托一个有名字的方法
Dele d = Cacl;
int result=d(4, 5);
Console.WriteLine(result);
//下面委托一个匿名方法
//定义一个委托,未实例化
Dele d1;
//给委托赋值一个匿名方法
d1 = delegate (int x,int y)
{
//同普通方法体一样,执行语句放在大括号里面
return (x + y) * 2;
};
//执行委托方法
result = d1(4,5);
Console.WriteLine(result);
}
static int Cacl(int x,int y)
{
return (x + y) * 2;
}
}
}
运行结果:
不难发现这个委托方法既然没有名字,那么只能通过委托的方式来调用。
三、隐式类型
匿名类:直接new出来一个{}的对象,编译器会自动命名一个类型给这个类。
声明格式:
var 变量名=new {属性1=值,属性2=值...};
1.隐式类型的局部变量
在C#3.0中,引进了一个新的关键字叫做var,var允许你声明一个新变量,它的类型是从用来初始化变量的表达式里隐式的推断出来的,即在声明的时候,你不需要给它定义类型,它会根据它的初始化器表达式来推断出它的类型,因此,我们称它为隐式类型。
注意:为了保证使用var关键字进行声明的变量的强类型特征,C#3.0要求必须对变量初始化,并且放到同一行,同样也不能为null。
示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
//定义两个属性一样,值不一样的匿名类
var a = new { age = 12, name = "w" };
var b = new { age = 13, name = "h" };
//定义一个属性一样,但顺序不一样的匿名类
var c = new { name="v",age = 13};
Console.WriteLine(a.GetType());
Console.WriteLine(b.GetType());
Console.WriteLine(c.GetType());
}
}
}
运行结果:
注意输出结果,属性位置不一样了,编译器分配的匿名类的名字也就不一样了。
2.隐式类型的数组
和隐式类型的局部变量一样,使用var关键字来替代数组类型,有了这个特性,将使我们创建数组的工作变得更加简单。我们可以直接使用"new[]"关键字来声明数组,后面跟上数组的初始化列表。在这里,也不行需要直接指定数组的类型,数组的类型是由初始化列表推断出来的。
示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program1
{
static void Main(string[] args)
{
//int[]
var a = new[] { 1, 10, 100 };
//string[]
var b = new[] { "1", "2", "3" };
//int型交错数组
var c = new[] {
new[] { 1, 10, 100 },
new[] { 1, 10, 100 }
};
Console.WriteLine(a.GetType());
Console.WriteLine(b.GetType());
Console.WriteLine(c.GetType());
}
}
}
运行结果:
四、Lambda表达式
定义:Lambda表达式实际上就是一种匿名函数,在Lambda表达式中可以包含语句以及运算符等操作,并且可用于创建委托或表达式目录制类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。使用Lambda表达式可大大减少代码量,使得代码更加的优美、简洁,更有可观性。
1.Lambda表达式的表现形式
表达式形式:(参数列表)=>表达式(或代码块)。在表达式左侧表示输入参数,右侧为响应的运算语句或者判断语句等,可包含函数调用等复杂方式。运算符=>读作goes to。
注:Lambda表达式对于编写Linq查询表达式特别有用,后面就会体验到
注:左边参数列表可以有多个参数,一个参数,或者无参数,参数类型可以隐式或者显式,参数类型可以忽略,因为可以根据使用的上下文进行推断而得到。
仅当Lambda只有一个输入参数时,括号才是可选的,否则括号是必须的。
例如:
(x,y)=>x*y //多参数,隐式类型=>表达式
x=>x*10 //单参数,隐式类型=>表达式
x=>{return x*10;} //单参数,隐式类型=>语句块
(int x)=>x*10 //单参数,显式类型=>表达式
(int x)=>{return x*10;} //单参数,显式类型=>语句块
()=>Console.Write(x) //无参数
2.Lambda表达式与委托类型
如下面的示例所示,你可以将此表达式分配给委托类型:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
public delegate double Del(double r);
static void Main(string[] args)
{
double vResult;
Del d1;
//匿名方法的返回值和形参要和Del委托是一致的
d1 = r => (4 / 3) * 3.14 * r * r * r;
//上面代码等价于下面的匿名方法
//d1 = delegate (double r)
//{
// return (4 / 3) * 3.14 * r * r * r;
//};
vResult = d1(4.4);
Console.WriteLine(vResult);
}
}
}
运行结果:
五、扩展方法
C#扩展方法:
- 方法所在的类必须是静态的
- 方法也必须是静态的
- 方法的第一个参数必须是你要扩展的那个类型,比如要给int类型扩展一个方法,那么第一个参数就必须是int。
- 在第一个参数前面还需要有一个this关键字。
扩展方法有以下几点总结:
- 可以向类中添加新方法,而不需要使用继承来创建新类,也不需要修改原有的类;
- 如果扩展方法与类中的方法有相同的签名,则扩展方法不会被调用,即:扩展方法会被被扩展类的同名方法覆盖,所以实现扩展方法我们需要承担随时被覆盖的风险(例如:如果扩展一个
string类中的ToString()。这时候扩展方法是无效的);- 扩展方法不能访问被扩展类的私有成员;
- 扩展方法只能使用实例来调用,不能像普通的静态方法一样使用类名调用;
- 只有引入扩展方法所在的命名空间后,扩展方法才可以使用;
现需要给一个类添加一个新方法,而不通过继承,这里我们就使用扩展方法来实现,以下是一个示例代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Student s = new Student();
s.Name = "小明";
s.Run();
s.ShowName(s);
}
}
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public void Show()
{
Console.WriteLine("我喜欢表演!");
}
}
/// <summary>
/// 这是放置扩展方法的静态类
/// </summary>
public static class StudentEx
{
/// <summary>
/// 扩展方法
/// </summary>
/// <param name="stu">该参数用于指明为哪个类提供的,参数前面必须使用this关键字</param>
public static void Run(this Student stu)
{
Console.WriteLine("我会跑步!");
}
public static void ShowName(this Student stu,Student s)
{
Console.WriteLine("我的名字是{0}", s.Name);
}
}
}
运行结果:
注:此示例代码可以看到扩展方法中有一个形参,而调用该扩展方法时,我们没有指明实参,是因为this关键字是指明该方法是提供给谁的,所以调用时是不需要该参数的。
六、yield关键字
yield关键字的作用是将当前集合中的元素立即返回。
- 返回元素用yield return;(一次一个的返回)。
- 结束返回用yield break;(终止迭代)当产生集合>达到某种条件的时候使用yield break,以终止继续创建集合
- 返回类型必须为lEnumerable、lEnumerable< T>、IEnumerator或IEnumerator< T>。lEnumerable枚举数,提供给在集合中进行数据的迭代,可以看做是一个集合。
迭代的意思是对于一个集合,可以逐一取出元素并遍历之。
下面通过一个简单的示例来理解一下yield关键字的用法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
//创建一个商品的集合
IList<Goods> list = new List<Goods>()
{
new Goods(){Name="薯条",Price=7.5},
new Goods(){Name="可乐",Price=3},
new Goods(){Name="鸡米花",Price=8},
new Goods(){Name="汉堡",Price=10},
new Goods(){Name="雪碧",Price=3}
};
//var 可用显式类型IEnumerable<Goods>替换
var obj = GetGoods(list, 5);
Console.WriteLine("超过5块的商品有:");
foreach(var item in obj)
{
Console.WriteLine(item.Name);
}
}
/// <summary>
/// 返回指定集合中满足条件的对象集合,这个集合是IEnumerable类型的,并且可以进行迭代
/// </summary>
public static IEnumerable<Goods> GetGoods(IList<Goods> list,double price)
{
foreach(var item in list)
{
if(item.Price>price)
{
//遍历集合中的元素,将价格大于price的元素返回
yield return item;
}
}
}
}
/// <summary>
/// 商品类
/// </summary>
public class Goods
{
public string Name { get; set; }
public double Price { get; set; }
}
}
运行结果:
在这做下说明:
通过以上代码可以看出yield关键字要配合IEnumerable类型进行使用,使用yield return 时,会把正在遍历的集合中的元素一个一个的返回到IEnumerable类型的集合中;使用yield break时,则停止返回,可以理解为循环中的跳出循环。
七、LINQ查询
1.什么是LINQ
Sql:通过数据库查询语句实现的数据库查询,缺点是开发效率低。
LINQ:实现在C#环境中直接通过linq语句实现对数据库的操作,通过编译器将linq语句编译成为底层的sql语句执行,提高了开发效率,更方便。
LINQ是指“语言集成查询”,其英文是指“Language Intergrated Query”。在C#中的LlNQ主要是指C#支持、实现的一种查询数据语法(及数据查询功能),这种数据查询语法的特点是,可以使用相同的语法访问不同的数据类型。
对于一个查询,如从一个List< T >集合中找出T的某个属性符合某一条件的所有元素的集合,可以使用List< T >的FindAll方法,或者直接遍历集合进行查询,也可以使用LINQ进行查询。LINQ的基本语法规范。
注意:一个Linq表达式只是定义了一个查询,但是并未执行,执行时间:当时通过foreach或者for循环执行Linq时才真正执行了linq表达式,这叫延迟加载。如果想立即查询,则只需要将整个linq表达式通过数据转换方法,转换为集合或数组,就会得到查询后的结果。
首先,一个LINQ查询:
var names = new List<string> { "Nic"", ""Jason", "Linda", "Linus","Nic"};
var query = from p in names
where p == "Nic"
select p;
foreach(var obj in query){
Console.Writeline(obj);//输出两行"Nic"
}
这里的 LINQ查询是从 names集合中,找出所有等于"Nic"的元素。这里query是一个lEnumerable(在这里,T是string型)型变量,frome p in names是一个from子句,where p=="Nic"是一个where子句,select p是一个select子句,三个子句构成了一个LINQ表达式,一个LINQ表达式返回一个变量赋值给query。我们称query是一个查询,或者说query指定了一个LINQ查询。
LINQ的基本语法规范:
预定义关键字:from,where,orderby,join,let,descending和select,group等。
表达式构成:必须以from子句开头,以select子句或者group子句结尾,在这两个子句中间,可以使用where,orderby,join,let,from子句。
如果你掌握了SQL,那么LINQ入门将会很容易。
使用上还需要注意的一些知识点:
- 一个LINQ表达式只是定义了一个查询,并未执行具体的查询。
- 具体的查询,需要foreach访问query时才会执行。
- 书写LINQ查询时有两种语法可供选择:方法语法(Fluent Syntax)和查询语法(Query Expression)。
1-1.条件查询和分组查询
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Product
{
public string ProductName { get; set; }
public string Category { get; set; }
public int Number { get; set; }
}
class Program
{
static void Main(string[] args)
{
//定义一个集合,集合和数组都实现接口IEnumerable,因此linq语句可以作用在集合和数组
IList<int> list = new List<int>() { 2, 5, 7, 8, 10 };
var result = from l in list
let a = l * 2
where a > 10
select a;
//let 如果查询之前需要对集合中的元素进行运算,就写在let后面,计算结果要使用一个变量存放
foreach(var item in result)
{
Console.WriteLine(item);
}
Console.WriteLine("=================================");
IEnumerable<Product> list1 = new List<Product>(){
new Product { ProductName = "李宁", Category = "鞋子",Number =100},
new Product { ProductName="耐克", Category="鞋子", Number = 1100 } ,
new Product { ProductName = "猫哆哩", Category = "食品", Number = 1300 },
new Product { ProductName = "可乐", Category = "饮料", Number = 21010 },
new Product { ProductName = "王老吉", Category = "饮料", Number = 2100 },
new Product { ProductName = "子弟土豆片", Category = "食品", Number = 1200 },
new Product { ProductName = "鲜橙多", Category = "饮料", Number = 4100 },
new Product { ProductName = "农夫山泉", Category = "饮料", Number = 5100 }};
//一个Linq表达式只是定义了一个查询,但是并未执行
//执行时间:当时通过foreach或者for循环执行Linq时才真正执行了linq表达式
//查询类别是饮料的产品信息
//p代表的是list1里面的每个元素,p代表的就是每一个product对象
var result1 = from p in list1
where p.Category == "饮料"
select p;
foreach (var item in result1)
{
Console.WriteLine("饮料产品:{0},数量:{1}" ,item.ProductName,item.Number);
}
Console.WriteLine("=================================");
//查询饮料产品且数量在5000以上的产品信息
//分组group以主体进行分组by分组的关键字
var result2 = from p in list1
group p by p.Category;
foreach(var item in result2)
{
Console.WriteLine("产品类别:{0},总分组数为:{1}", item.Key, item.Count());
foreach (var it in item)
{
Console.WriteLine("产品名称:{0},产品数量:{1}", it.ProductName, it.Number);
}
}
}
}
}
运行结果:
1-2.into关键字的使用,以及自定义查询结果
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Product
{
public string ProductName { get; set; }
public string Category { get; set; }
public int Number { get; set; }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<Product> list1 = new List<Product>(){
new Product { ProductName = "李宁", Category = "鞋子",Number =100},
new Product { ProductName="耐克", Category="鞋子", Number = 1100 } ,
new Product { ProductName = "猫哆哩", Category = "食品", Number = 1300 },
new Product { ProductName = "可乐", Category = "饮料", Number = 21010 },
new Product { ProductName = "王老吉", Category = "饮料", Number = 2100 },
new Product { ProductName = "子弟土豆片", Category = "食品", Number = 1200 },
new Product { ProductName = "鲜橙多", Category = "饮料", Number = 4100 },
new Product { ProductName = "农夫山泉", Category = "饮料", Number = 5100 }};
//查询各个分类的名称及数量,只希望得到两列数据(分类名称和数量)
var result3 = from p in list1
group p by p.Category into temp
select new { Title=temp.Key,Num=temp.Count()};
foreach (var item in result3)
{
Console.WriteLine("产品类别:{0},总分组数为:{1}", item.Title, item.Num);
}
}
}
}
运行结果:
1-3.对查询结果排序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Product
{
public string ProductName { get; set; }
public string Category { get; set; }
public int Number { get; set; }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<Product> list1 = new List<Product>(){
new Product { ProductName = "李宁", Category = "鞋子",Number =100},
new Product { ProductName="耐克", Category="鞋子", Number = 1100 } ,
new Product { ProductName = "猫哆哩", Category = "食品", Number = 1300 },
new Product { ProductName = "可乐", Category = "饮料", Number = 21010 },
new Product { ProductName = "王老吉", Category = "饮料", Number = 2100 },
new Product { ProductName = "子弟土豆片", Category = "食品", Number = 1200 },
new Product { ProductName = "鲜橙多", Category = "饮料", Number = 4100 },
new Product { ProductName = "农夫山泉", Category = "饮料", Number = 5100 }};
//排序 order by 排序的关键词,默认升序,加入descending关键字则降序
var result4 = from p in list1
orderby p.Number descending
select p;
foreach (var item in result4)
{
Console.WriteLine("产品名称:{0},产品数量:{1}", item.ProductName, item.Number);
}
}
}
}
运行结果:
注意:排序中默认是升序,关键字ascending可以不写,若要降序则需要写上descending。
2.方法语法的链接查询
示例代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
string[] names = { "Tom","Dick","Harry","Marry","Jay"};
IEnumerable<string> query = names.Where(n => n.Contains("a")).
OrderBy(n => n.Length).
Select(n => n.ToUpper());
foreach (var item in query)
{
Console.WriteLine(item);
}
}
}
}
运行结果:
上面代码的功能就是将数组中含有字符a的元素查询出来并转换成大写字母。
将之前的示例代码通过方法语法的方式进行修改
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Product
{
public string ProductName { get; set; }
public string Category { get; set; }
public int Number { get; set; }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<Product> list1 = new List<Product>(){
new Product { ProductName = "李宁", Category = "鞋子",Number =100},
new Product { ProductName="耐克", Category="鞋子", Number = 1100 } ,
new Product { ProductName = "猫哆哩", Category = "食品", Number = 1300 },
new Product { ProductName = "可乐", Category = "饮料", Number = 21010 },
new Product { ProductName = "王老吉", Category = "饮料", Number = 2100 },
new Product { ProductName = "子弟土豆片", Category = "食品", Number = 1200 },
new Product { ProductName = "鲜橙多", Category = "饮料", Number = 4100 },
new Product { ProductName = "农夫山泉", Category = "饮料", Number = 5100 }};
//一个Linq表达式只是定义了一个查询,但是并未执行
//执行时间:当时通过foreach或者for循环执行Linq时才真正执行了linq表达式
//查询类别是饮料的产品信息
//p代表的是list1里面的每个元素,p代表的就是每一个product对象
var result1 = list1.Where(p=>p.Category=="饮料");
foreach (var item in result1)
{
Console.WriteLine("饮料产品:{0},数量:{1}", item.ProductName, item.Number);
}
Console.WriteLine("=================================");
//查询饮料产品且数量在5000以上的产品信息
//分组group以主体进行分组by分组的关键字
var result2 = list1.GroupBy(p=>p.Category);
foreach (var item in result2)
{
Console.WriteLine("产品类别:{0},总分组数为:{1}", item.Key, item.Count());
foreach (var it in item)
{
Console.WriteLine("产品名称:{0},产品数量:{1}", it.ProductName, it.Number);
}
}
Console.WriteLine("=================================");
//查询各个分类的名称及数量,只希望得到两列数据(分类名称和数量)
var result3 = list1.GroupBy(p => p.Category).
Select(p => new { Title = p.Key, Num = p.Count() });
foreach (var item in result3)
{
Console.WriteLine("产品类别:{0},总分组数为:{1}", item.Title, item.Num);
}
Console.WriteLine("=================================");
//排序 order by 排序的关键词,默认升序,加入descending关键字则降序
var result4 = list1.OrderByDescending(p=>p.Number);
foreach (var item in result4)
{
Console.WriteLine("产品名称:{0},产品数量:{1}", item.ProductName, item.Number);
}
}
}
}
运行结果:
可以看到结果和linq表达式的结果一样,linq的方法语法查询其实更符合我们的编写习惯。
针对上面的几个方法做如下解释:
- 在VS中可以看到Where,OrderBy,Select这几个方法都是Linq对IEnumerable接口的扩展方法,由于数组和集合都实现了IEnumerable接口,所以数组和集合也是可以使用的。
- 扩展方法中放的是一个lambda表达式,也就是一个匿名函数,转到定义可以看到,这些扩展方法的参数其实是一个委托,所以才将匿名函数作为参数。
- 当链接的使用查询运算符时,一个运算符的输出Sequence会成为下一个运算符的输入Sequence,其结果形成了一个Sequence的传输链。
2-1.对方法Where实现的研究
示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp4
{
public class Product
{
public string ProductName { get; set; }
public string Category { get; set; }
public int Number { get; set; }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<Product> list1 = new List<Product>(){
new Product { ProductName = "李宁", Category = "鞋子",Number =100},
new Product { ProductName="耐克", Category="鞋子", Number = 1100 } ,
new Product { ProductName = "猫哆哩", Category = "食品", Number = 1300 },
new Product { ProductName = "可乐", Category = "饮料", Number = 21010 },
new Product { ProductName = "王老吉", Category = "饮料", Number = 2100 },
new Product { ProductName = "子弟土豆片", Category = "食品", Number = 1200 },
new Product { ProductName = "鲜橙多", Category = "饮料", Number = 4100 },
new Product { ProductName = "农夫山泉", Category = "饮料", Number = 5100 }};
//使用我们自己写的where方法和select
var result1 = list1.MyWhere(p => p.Category == "饮料").
MySelect(p=>p);
foreach (var item in result1)
{
Console.WriteLine("饮料产品:{0},数量:{1}", item.ProductName, item.Number);
}
Console.WriteLine("=================================");
//使用系统的where方法和select
var result2 = list1.Where(p => p.Category == "饮料").
Select(p => p.ProductName);
foreach (var item in result1)
{
Console.WriteLine("饮料产品:{0},数量:{1}", item.ProductName, item.Number);
}
Console.WriteLine("=================================");
//使用系统的where方法和select
var result3 = list1.Where(p => p.Category == "饮料").
Select(p => p.ProductName);
foreach (var item in result3)
{
Console.WriteLine("饮料产品:{0}", item);
}
Console.WriteLine("=================================");
//使用自己的where方法和select
var result4 = list1.MyWhere(p => p.Category == "饮料").
MySelect(p => p.ProductName);
foreach (var item in result4)
{
Console.WriteLine("饮料产品:{0}", item);
}
}
}
/// <summary>
/// 静态类,做扩展方法用
/// </summary>
public static class ListEx
{
/// <summary>
/// 自写Where方法的内部结构
/// </summary>
/// <param name="lis">扩展类型</param>
/// <param name="func">系统自带的一个委托</param>
/// <returns></returns>
public static IEnumerable<Product> MyWhere(this IEnumerable<Product> lis, Func<Product, bool> func)
{
foreach (var item in lis)
{
//这里的func相当于下面的这个lambda表达式
//p => p.Category == "饮料"
//将满足条件的item对象返回到IEnumerable中
if (func(item))
{
yield return item;
}
}
}
/// <summary>
/// 自写Selecct方法的内部结构
/// </summary>
/// <param name="lis">扩展类型</param>
/// <param name="func">系统自带的一个委托</param>
/// <returns></returns>
public static IEnumerable<Product> MySelect(this IEnumerable<Product> lis, Func<Product, Product> func)
{
foreach (var item in lis)
{
//这里的func相当于下面的这个lambda表达式
//p => p
//将item对象返回到IEnumerable中
yield return func(item);
}
}
/// <summary>
/// 自写Selecct方法,这是一个重载,只返回一个属性
/// </summary>
/// <param name="lis">扩展类型</param>
/// <param name="func">系统自带的一个委托</param>
/// <returns></returns>
public static IEnumerable<string> MySelect(this IEnumerable<Product> lis, Func<Product, string> func)
{
foreach (var item in lis)
{
//这里的func相当于下面的这个lambda表达式
//p => p.ProductName
//将item对象返回到IEnumerable中
yield return func(item);
}
}
}
}
运行结果:
分析:
两次运行结果一致,通过手写Where方法和Select方法,能更好的理解中间放lambda表达式的原因,以及实现过程。
2-2使用方法语法做查询练习
示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp5
{
public class Student
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
public IEnumerable<int> Scores { get; set; }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<Student> list = new List<Student>()
{
new Student { Name ="张三",Sex ="男",Age=23,Scores =new List<int>() { 98,76,89,54}},
new Student { Name ="李四", Sex ="男" ,Age=33,Scores =new List<int>(){ 89,76,89,45 } },
new Student { Name ="王五" ,Sex ="男" ,Age=43,Scores =new List<int>() {98, 76, 89, 55} },
new Student { Name ="刘一",Sex ="男",Age=23,Scores =new List<int>(){ 67,76,89,33} },
new Student { Name = "夏天", Sex = "女", Age = 13, Scores = new List<int>() { 89, 76, 89, 56 } },
new Student { Name = "薇薇", Sex = "女", Age = 23, Scores = new List<int>(){ 47, 76, 89, 67 } },
new Student { Name = "肖小", Sex = "女", Age = 13, Scores = new List<int>() { 36, 76, 89, 48 } },
new Student { Name = "武大", Sex = "男", Age = 63, Scores = new List<int>(){ 89, 76, 89, 69 } } };
//统计班级男女生的人数,并保存到集合=》ListResult中
var result = list.GroupBy(s => s.Sex).
Select(s => new { Sex = s.Key, Num = s.Count() });
//下面是等价的表达式语法查询
//from a in list
//group a by a.Sex into temp
//select new { Sex = temp.Key, Num = temp.Count() };
foreach (var item in result)
{
Console.WriteLine("性别:{0},人数:{1}", item.Sex, item.Num);
}
Console.WriteLine("==================================");
//计算男生同学的总分和平均分
var result1 = list.Where(s => s.Sex == "男").
Select(s => new { Name = s.Name, Sum = s.Scores.Sum(), Avg = s.Scores.Average() });
foreach (var item in result1)
{
Console.WriteLine("姓名:{0},总分:{1},平均分:{2}", item.Name, item.Sum,item.Avg);
}
Console.WriteLine("==================================");
//计算男生,年纪30以上的同学的最高分和最低分,并对计算的最高分进行降序排序
var result2 = list.Where(s => s.Sex == "男" && s.Age > 30).
OrderByDescending(s=>s.Scores.Max()).
Select(s => new { Name = s.Name,Age=s.Age, Max = s.Scores.Max(), Min = s.Scores.Min() });
foreach (var item in result2)
{
Console.WriteLine("姓名:{0},年龄:{1},最高分:{2},最低分:{3}", item.Name,item.Age, item.Max, item.Min);
}
}
}
}
运行结果:
上述代码可以自行修改为使用linq表达式的结构来进行查询,方法同理。
2-3元素操作
方法名 | 说明 |
---|---|
ElementAt | 返回集合中指定索引处的元素 |
ElementAtOrDefault | 返回集合中指定索引处的元素,如果索引超出范围则返回默认值 |
First | 返回集合中的第一个元素或满足条件的第一个元素 |
FirstOrDefault | 返回集合中的第一个元素或满足条件的第一个元素,如果没有则返回默认值 |
Last | 返回集合中的最后一个元素或满足条件的最后一个元素 |
LastOrDefault | 返回集合中的最后一个元素或满足条件的最后一个元素,如果没有则返回默认值 |
Single | 返回集合中的唯一元素或满足条件的唯一元素 |
SingleOrDefault | 返回集合中的唯一元素或满足条件的唯一元素,如果没有,则返回默认值 |
2-4数据类型转换操作的使用
方法名 | 说明 |
---|---|
ToList | 将集合转换为List |
ToArray | 将集合转换为数组 |
ToDictionary | 根据键选择器函数将元索放入Dictionary<TKey,Tvalue>中 |
ToLookup | 根据键选择器函数将元索放入Lookup<Tkey,Telement>中 |
AsEnumerable | 将一个序列转换为IEnumerable集合 |
AsQueryable | 将lEnumerable转换为IQueryable |
Cast | 将集合的元素强制转换为指定类型 |
OfType | 根据值强制转换为指定类型的能力筛选值 |
3LINQ总结
LINQ主要关键字: from、where、select、group、orderby。
方法语法的本质是通过扩展方法和Lambda表达式来创建查询。
聚合操作的主要方法:Count、Max、Min、Sum、Average。
数据类型转换操作的主要方法:ToList、ToArray、AsEnumerable、Cast、OfType。
八、EF的基本使用
1.什么是ORM
ORM全称是“对象关系映射”(Object-Relation Mapping)。
ORM是将关系数据库中的数据用对象的形式表现出来,并通过面向对象的方式将对象组织起来,实现系统业务逻辑的过程。