第二章:Action参数及ActionResult
第三章:layout-Html辅助方法-数据验证
5.如何判断请求来自ajax
hmtl代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>AJAX</title> <script src="~/jquery-1.8.3.js"></script> <div> <script type="text/javascript"> $(function () { $("#btn").click(function () { $.ajax({ dataType: "json", type: "GET", url: "/Test/AJAX2", success: function (res) { alert(res); alert(res.msg); }, error: function (err) { alert("提交异常"); alert(err.msg); }, }); }); }); </script> </div> </head> <body> <div> <input type="button" id="btn" value="点我"/> </div> </body> </html>
c#代码(注意,如果ajax是get请求,c#得这样设置Json(new { state = "1001", msg = "err" }, JsonRequestBehavior.AllowGet),,,post无视)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace _03_05_如果判断请求来自ajax.Controllers { public class TestController : Controller { // GET: Test public ActionResult Index() { return View(); } public ActionResult AJAX() { return View(); } public ActionResult AJAX2() { if (Request.IsAjaxRequest()) { return Json(new { state = "1000", msg = "ok" }, JsonRequestBehavior.AllowGet); } else { return Json(new { state = "1001", msg = "err" }, JsonRequestBehavior.AllowGet); } } } }
6.数据验证基础
获取验证错误信息
c#
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
// GET: Val1 public ActionResult Index(IndexModel model) { //参数校验是否通过 if (ModelState.IsValid) { return Content("Age=" + model.Age); } else { string msg = MVCHelper.GetValidMsg(ModelState); return Content("err->" + msg); } }
MVCHelper
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; namespace _03_06_数据验证基础 { public static class MVCHelper { public static string GetValidMsg(ModelStateDictionary modelState) // 有两个ModelStateDictionary 类,别弄混乱了。要使用 System.Web.Mvc 下的 { StringBuilder sb = new StringBuilder(); foreach (var key in modelState.Keys) { if (modelState[key].Errors.Count <= 0) { continue; } sb.Append("属性【").Append(key).Append("】错误:"); foreach (var modelError in modelState[key].Errors) { sb.AppendLine(modelError.ErrorMessage); } } return sb.ToString(); } } }
7.数据验证Attribute
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/* ASP.Net MVC 提供了在服务器端验证请求数据的能力。要把对应的 Attribute 标记到 Model 的属性上(标记到方法参数上很多地方不起作用)。 常用验证 Attribute: a) [Required] 这个属性是必须的 b) [StringLength(100)],字符串最大长度 100;[StringLength(100,MinimumLength=10)] 长度要介于 10 到 100 之间 c) [RegularExpression(@"aa(\d)+bb")] 正则表达式 d) [Range(35,88)] 数 值 范 围 。 字 符 串 长 度 范 围 的 话 请 使 用 [StringLength(100,MinimumLength=10)] e) [Compare("Email")]这个属性必须和 Email 属性值一样。 f) [EmailAddress] 要是邮箱地址 g) [Phone]电话号码,规则有限 3)验证 Attribute 上都有 ErrorMessage 属性,用来自定义报错信息。ErrorMessage 中可以用 {0}占位符作为属性名的占位。 4)数据验证+Html 辅助类高级控件可以实现很多简化的开发,连客户端+服务器端校验都自 动实现了,但是有点太“WebForm”了,因此这里先学习核心原理,避免晕菜。 */ public class IndexModel { public int Age { get; set; } public long Id { get; set; } [Required(ErrorMessage ="手机号必填")] public string PhoneNum { get; set; } public string PassWord { get; set; } [Compare("PassWord", ErrorMessage = "两次密码输入的必须一致")] public string PassWord2 { get; set; } [EmailAddress(ErrorMessage = "请输入正确的Email地址")] public string Email { get; set; } }
8.自定义验证规则
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class IndexModel { public int Age { get; set; } public long Id { get; set; } [CNPhoneNum] public string PhoneNum { get; set; } public string PassWord { get; set; } [Compare("PassWord", ErrorMessage = "两次密码输入的必须一致")] public string PassWord2 { get; set; } [EmailAddress(ErrorMessage = "请输入正确的Email地址")] public string Email { get; set; } [QQNumber] public string QQ { get; set; } }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class CNPhoneNumAttribute : ValidationAttribute { public CNPhoneNumAttribute() { this.ErrorMessage = "电话号码必须是固话或者手机,固话要是3-4位区号开头,手机必须以13、15、18、17开头"; } //注意,不要override ValidationResult IsValid(object value, ValidationContext validationContext) public override bool IsValid(object value) { if (value is string) { string s = (string)value; if (s.Length == 13)//手机号 { if (s.StartsWith("13") || s.StartsWith("15") || s.StartsWith("17") || s.StartsWith("18")) { return true; } else { return false; } } else if (s.Contains("-"))//固话 { //010,021 0755 0531 string[] strs = s.Split('-'); if (strs[0].Length == 3 || strs[0].Length == 4) { return true; } else { return false; } } else { return false; } } else { return false; } } }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class QQNumberAttribute:RegularExpressionAttribute { public QQNumberAttribute():base(@"^\d{5,11}$") { this.ErrorMessage = "字段{0}不是合法的QQ号,QQ号长度在5-11位"; } }
第四章:Filter和NuGet
过滤器Filter
AOP(面向切面编程)是一种架构思想,用于把公共的逻辑放到一个单独的地方,这样 就不用每个地方都写重复的代码了。比如程序中发生异常,不用每个地方都 try...catch...只 要在(Global 的 Application_Error)中统一进行异常处理。不用每个 Action 中都检查当前 用户是否有执行权限,ASP.net MVC 中提供了一个机制,每个 Action 执行之前都会执行我们的代码,这样统一检查即可。 一夫当关万夫莫开! 1、四种 Filter 在 ASP.Net MVC 中提供了四个 Filter(过滤器)接口实现了这种 AOP 机制: IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilter。 1)IAuthorizationFilter 一般用来检查当前用户是否有 Action 的执行权限,在每个 Action 被 执行前执行 OnAuthorization 方法; 2)IActionFilter 也是在每个 Action 被执行前执行 OnActionExecuting 方法,每个 Action 执行 完成后执行 OnActionExecuted 方法。和 IAuthorizationFilter 的区别是 IAuthorizationFilter 在 IActionFilter 之前执行,检查权限一般写到 IAuthorizationFilter 中; 3)IResultFilter,在每个 ActionResult 的前后执行 IResultFilter。用的很少,后面有一个应用。 4)IExceptionFilter,当 Action 执行发生未处理异常的时候执行 OnException 方法。在 ASP.net MVC 中仍然可以使用“Global 的 Application_Error”,但是建议用 IExceptionFilter。 定义的类可以在Global中GlobalFilters.Filters.Add(new XXXFilter());的方式添加为全局的 过滤器。 2、IAuthorizationFilter 案例:只有登录后才能访问除了 LoginController 之外的 Controller。 1) 编写一个类 CheckAuthorFilter,实现 IAuthorizationFilter 接口 2) 在 Global 中注册这个 Filter:GlobalFilters.Filters.Add(new CheckAuthorFilter()); 3) CheckAuthorFilter 中实现 OnAuthorization 方法。filterContext.ActionDescriptor 可以获得 Action 的信息:filterContext.ActionDescriptor.ActionName 获得要执行的 Action 的名字; filterContext.ActionDescriptor.ControllerDescriptor.ControllerName 为要执行的 Controller 的名字;filterContext.ActionDescriptor.ControllerDescriptor.ControllerType 为要执行的 Controller 的 Type;filterContext.HttpContext 获得当前请求的 HttpContext;如果给 “ filterContext.Result ”赋值了,那么就不会再执行 要执行的 Action ,而是以 “ filterContext.Result ” 的 值 作 为 执 行 结 果 ( 注 意 如 果 是 执 行 的 filterContext.HttpContext.Response.Redirect(),那么目标 Action 还会执行的)。 4) 检查当前用户是否登录,如果没有登录则 filterContext.Result = new ContentResult() { Content = "没有权限" }; 或者 filterContext.Result = new RedirectResult("/Login/Index"); (最好不要 filterContext.HttpContext.Response.Redirect("/Login/Index");) 5) A 用户有一些 Action 执行权限,B 用户有另外一些 Action 的执行权限; 3、IActionFilter 案例:日志记录,记录登录用户执行的 Action 的记录,方便跟踪责任。 4、IExceptionFilter 案例:记录未捕获异常。 public class ExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext filterContext) { File.AppendAllText("d:/error.log", filterContext.Exception.ToString()); filterContext.ExceptionHandled = true;//如果有其他的 IExceptionFilter 不再执行 filterContext.Result = new ContentResult() { Content= "error" }; } } 然后 GlobalFilters.Filters.Add(new ExceptionFilter()); 5、总结好处:一次编写,其他地方默认就执行了。可以添加多个同一个类型的全局 Filter, 按照添加的顺序执行。 6、(*)非全局 Filter:只要让实现类继承自 FilterAttribute 类,然后该实现哪个 Filter 接口就 实现哪个(四个都支持)。不添加到 GlobalFilters 中,而是把这个自定义 Attribute 添加到 Controller 类上这样就只有这个 Controller 中操作会用到这个 Filter。如果添加到 Action 方法 上,则只有这个 Action 执行的时候才会用到这个 Filter。
Global.asax
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); GlobalFilters.Filters.Add(new CheckLoginFilter()); } }
CheckLoginFilter
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace _04_02_登录校验Filter.Filters { public class CheckLoginFilter : IAuthorizationFilter { public void OnAuthorization(AuthorizationContext filterContext) { string ctrlName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = filterContext.ActionDescriptor.ActionName; if(ctrlName=="Login" && (actionName=="Index" ||actionName=="Login")) { //什么都不做 } else//检查登录状态 { if (filterContext.HttpContext.Session["username"] == null) { //1.返回内容 //ContentResult contentResult = new ContentResult(); //contentResult.Content = "没有登录"; //如果在Filter中给FilterContext.Result 赋值了,那么将不再继续执行要执行的Filter,而是把FilterContext.Result执行,返回给用户 //filterContext.Result = contentResult; //2.重定向 filterContext.Result = new RedirectResult("~/Login/Index"); } } } } }
IActionFilter
Global.asax
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); GlobalFilters.Filters.Add(new LogActionFilter()); }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; using System.Web.Mvc; namespace _04_02_登录校验Filter.Filters { public class LogActionFilter : IActionFilter { public void OnActionExecuted(ActionExecutedContext filterContext) { string ctrlName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = filterContext.ActionDescriptor.ActionName; string path = filterContext.HttpContext.Server.MapPath("~/Log.txt"); File.AppendAllText(path, "执行了" + ctrlName + "." + actionName); } public void OnActionExecuting(ActionExecutingContext filterContext) { string ctrlName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = filterContext.ActionDescriptor.ActionName; string path = filterContext.HttpContext.Server.MapPath("~/Log.txt"); File.AppendAllText(path,"将要执行"+ctrlName+"."+actionName); } } }
第五章:Linq与EF入门
lambda
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NuGet { class Program { static void Main(string[] args) { //lambda改造GetMax int[] nums = new int[] { 3, 88, 6, 9 }; int m = GetMax(nums, (i1, i2) => i1 > i2); Console.WriteLine(m); //lambda在EF中 List<person> list = new List<person>(); list.Where(p => p.name==""); Console.ReadKey(); } static T GetMax<T>(T[] objs, Func<T, T, bool> ComareFunc) { T max = objs[0]; for (int i = 0; i < objs.Length; i++) { if (ComareFunc(objs[i], max)) { max = objs[i]; } } return max; } } public class person { public string name { get; set; } } }
集合高级扩展方法
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
二、 高级集合扩展方法 //学生 public class Person { public string Name { get; set; } public int Age { get; set; } public bool Gender { get; set; } public int Salary { get; set; } public override string ToString() { return string.Format("Name={0},Age={1},Gender={2},Salary={3}", Name, Age, Gender, Salary); } } //老师 public class Teacher { public Teacher() { this.Students=new List<Person>(); } public string Name { get; set; } public List<Person> Students { get; set; } } var s0 =new Person { Name="tom",Age=3,Gender=true,Salary=6000}; var s1 = new Person { Name = "jerry", Age = 8, Gender = true, Salary = 5000 }; var s2 = new Person { Name = "jim", Age = 3, Gender = true, Salary = 3000 }; var s3 = new Person { Name = "lily", Age = 5, Gender = false, Salary = 9000 }; var s4 = new Person { Name = "lucy", Age = 6, Gender = false, Salary = 2000 }; var s5 = new Person { Name = "kimi", Age = 5, Gender = true, Salary = 1000 }; List<Person> list = new List<Person>(); list.Add(s0); list.Add(s1); list.Add(s2); list.Add(s3); list.Add(s4); list.Add(s5); Teacher t1 = new Teacher { Name="如鹏网张老师"}; t1.Students.Add(s1); t1.Students.Add(s2); Teacher t2 = new Teacher { Name = "如鹏网刘老师" }; t2.Students.Add(s2); t2.Students.Add(s3); t2.Students.Add(s5); Teacher[] teachers = { t1,t2}; 1. Any(),判断集合是否包含元素,返回值是 bool,一般比 Coun()>0 效率高。Any 还可以 指 定 条 件 表 达 式 。 bool b = list.Any(p => p.Age > 50); 等价于 bool b = list.Where(p=>p.Age>50).Any(); 2. Distinct(),剔除完全重复数据。(*)注意自定义对象的 Equals 问题:需要重写 Equals 和 GetHashCode 方法来进行内容比较。 3. 排序:升序 list.OrderBy(p=>p.Age);降序 list.OrderByDescending(p=>p.Age)。指定多个排序规 则,而不是多个 OrderBy,而是:list.OrderByDescending(p=>p.Age).ThenBy(p=>p.Salary),也支 持 ThenByDescending()。注意这些操作不会影响原始的集合数据。 4. Skip(n)跳过前 n 条数据;Take(n)获取最多 n 条数据,如果不足 n 条也不会报错。常用来 分页获取数据。list.Skip(3).Take(2)跳过前 3 条数据获取 2 条数据。 5. Except(items1)排除当前集合中在 items1 中存在的元素。用 int 数组举例。 6. Union(items1)把当前集合和 items1 中组合。用 int 数组举例。 7. Intersect(items1) 把当前集合和 items1 中取交集。用 int 数组举例。 8. 分组: foreach(var g in list.GroupBy(p => p.Age)) { Console.WriteLine(g.Key+":"+g.Average(p=>p.Salary)); } 9. SelectMany:把集合中每个对象的另外集合属性的值重新拼接为一个新的集合 foreach(var s in teachers.SelectMany(t => t.Students)) { Console.WriteLine(s);//每个元素都是 Person } 注意不会去重,如果需要去重要自己再次调用 Distinct() 10. Join class Master { public long Id { get; set; } public string Name { get; set; } } class Dog { public long Id { get; set; } public long MasterId { get; set; } public string Name { get; set; } } Master m1 = new Master { Id = 1, Name = "杨中科" }; Master m2 = new Master { Id = 2, Name = "比尔盖茨" }; Master m3 = new Master { Id = 3, Name = "周星驰" }; Master[] masters = { m1,m2,m3}; Dog d1 = new Dog { Id = 1, MasterId = 3, Name = "旺财" }; Dog d2 = new Dog { Id = 2, MasterId = 3, Name = "汪汪" }; Dog d3 = new Dog { Id = 3, MasterId = 1, Name = "京巴" }; Dog d4 = new Dog { Id = 4, MasterId = 2, Name = "泰迪" }; Dog d5 = new Dog { Id = 5, MasterId = 1, Name = "中华田园" }; Dog[] dogs = { d1, d2, d3, d4, d5 }; Join 可以实现和数据库一样的 Join 效果,对有关联关系的数据进行联合查询 下面的语句查询所有 Id=1 的狗,并且查询狗的主人的姓名。 var result = dogs.Where(d => d.Id > 1).Join(masters, d => d.MasterId, m => m.Id, (d,m)=>new {DogName=d.Name,MasterName=m.Name}); foreach(var item in result) { Console.WriteLine(item.DogName+","+item.MasterName); }
linq
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
以 from item in items 开始,items 为待处理的集合,item 为每一项的变量名; 最后要加上 select,表示结果的数据;记得 select 一定要最后。这是刚用比较别扭的地 方。 看各种用法,不用解析: 1) var r= from d in dogs select d.Id; 2) var r = from d in dogs select new{d.Id,d.Name,Desc="一条狗"}; 3) 排序 var items = from d in dogs //orderby d.Age //orderby d.Age descending orderby d.Age,d.MasterId descending select d; 4) join var r9 = from d in dogs join m in masters on d.MasterId equals m1.Id select new { DogName=d.Name,MasterName=m.Name}; 注意 join 中相等不要用==,要用 equals。 写 join 的时候 linq 比“lambda” 漂亮 5) group by var r1 = from p in list group p by p.Age into g select new { Age = g.Key, MaxSalary = g.Max(p=>p.Salary), Count = g.Count() }; 4、混用 只有 Where,Select,OrderBy,GroupBy,Join 等这些能用 linq 写法,如果要用下面的 “ Max,Min,Count,Average,Sum,Any,First,FirstOrDefault,Single,SingleOrDefault,Distinct,Skip,Ta ke 等”则还要用 lambda 的写法(因为编译后是同一个东西,所以当然可以混用)。 var r1 = from p in list group p by p.Age into g select new { Age = g.Key, MaxSalary = g.Max(p=>p.Salary), Count = g.Count() }; int c = r1.Count(); var item = r1.SingleOrDefault(); var c = (from p in list where p.Age>3 select p ).Count(); lambda 对 linq 说:论漂亮我不行,论强大你不行!
EntityFramework
使用 EF 进行数据库开发的时候有两个东西建:建数据库(T_Persons),建模型类 (Person)。根据这两种创建的先后顺序有 EF 的三种创建方法: a) DataBase First(数据库优先):先创建数据库表,然后自动生成 EDM 文件,EDM 文件生成模型类。简单展示一下 DataBase First 的使用。 b) Model First(模型优先):先创建 Edm 文件,Edm 文件自动生成模型类和数据库; c) Code First(代码优先):程序员自己写模型类,然后自动生成数据库。没有 Edm。
CodeFirst(DataAnnotations方式)
1.创建表
2.通过NuGet安装EntityFramework.添加连接字符串
3.创建数据库对应模型类,主键必须Id
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _05_08_CodeFirst基本使用 { [Table("T_Persons")] public class Person { public long Id { get; set; } public string Name { get; set; } public DateTime CreateDateTime { get; set; } } }
4.创建继承DbContext的类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _05_08_CodeFirst基本使用 { class TestDbContext:DbContext { public TestDbContext() : base("name=connStr") { } public DbSet<Person> Persons { get; set; } } }
EF 模型的两种配置方式 EF 中的模型类的配置有 DataAnnotations、FluentAPI 两种。
上面这种在模型类上 [Table("T_Persons")]、[Column("Name")]这种方式就叫 DataAnnotations
这种方式比较方便,但是耦合度太高,一般的类最好是 POCO(Plain Old C# Object,没 有继承什么特殊的父类,没有标注什么特殊的 Attribute,没有定义什么特殊的方法, 就是一堆普通的属性);不符合大项目开发的要求。微软推荐使用 FluentAPI 的使用方式, 因此后面主要用 FluentAPI 的使用方式。
FluentAPI 配置 T_Persons 的方式
1. 数据库中建表 T_Perons,有 Id(主键,自动增长)、Name、CreateDateTime 字段。
2. 创建 Person 类。模型类就是普通 C#类
public class Person
{
public long Id { set; get; }
public string Name { get; set; }
public DateTime CreateDateTime { get; set; }
}
3. 创建一个 PersonConfig 类,放到 ModelConfig 文件夹下(PersonConfig、EntityConfig 这样的名字都不是必须的)
class PersonConfig: EntityTypeConfiguration<Person>
{
public PersonConfig()
{
this.ToTable("T_Persons");//等价于[Table("T_Persons")]
}
}
4. 创建 DbContext 类
public class MyDbContext:DbContext
{
public MyDbContext():base("name=conn1")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.AddFromAssembly(
Assembly.GetExecutingAssembly());
}
public DbSet<Person> Persons { get; set; }
}
下面这句话: modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly()); 代表从这句话所在的程序集加载所有的继承自 EntityTypeConfiguration 为模型配置类。 还有很多加载配置文件的做法(把配置写到 OnModelCreating 中或者把加载的代码写 死到 OnModelCreating 中),但是这种做法是最符合大项目规范的做法。 和以前唯一的不同就是:模型不需要标注 Attribute;编写一个 XXXConfig 类配置映射关系; DbContext 中 override OnModelCreating;
5. 以后的用法和以前一样。
6. 多个表怎么办?创建多个表的实体类、Config 类,并且在 DbContext 中增加多个 DbSet 类型的属性即可。
第六章:Entity Framework 原理和状态管理
1.SQL监控
using(MYDbContext ctx=new MYDbContext())
{
ctx.Database.Log = (sql) => { Console.WriteLine(sql); };
}
EF 的查询是“延迟执行”的,只有遍历结果集的时候才执行 select 查询,ToList()内部也 是遍历结果集形成 List
EF 中还可以多次指定 where 来实现动态的复合检索:
//必须写成 IQueryable,如果写成 IEnumerable 就会在内存中取后续数据
IQueryable items = ctx.Persons;//为什么把 IQueryable换成 var 会编译出错
items = items.Where(p=>p.Name=="rupeng");
items = items.Where(p=>p.Id>5);
每次开始执行的__MigrationHistory 等这些 SQL 语句是什么?是 DBMigration 用的,也就 是由 EF 帮我们建数据库,现在我们用不到,用下面的代码禁用: Database.SetInitializer(null); XXXDbContext 就是项目 DbContext 的类名。一般建议放到 XXXDbContext 构造函数中。 注意这里的 Database 是 System.Data.Entity 下的类,不是 DbContext 的 Database 属性。如果 写到 DbContext 中,最好用上全名,防止出错。
public class MYDbContext : DbContext { public MYDbContext() : base("name=connStr") { System.Data.Entity.Database.SetInitializer<MYDbContext>(null); } public DbSet<Person> Persons { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly()); } }
2.EntityFramework执行原始 SQL
不要“手里有锤子,到处都是钉子”! 在一些特殊场合,需要执行原生 SQL。 执行非查询语句,调用 DbContext 的 Database 属性的 ExecuteSqlCommand 方法,可以 通过占位符的方式传递参数: ctx.Database.ExecuteSqlCommand("update T_Persons set Name={0},CreateDateTime=GetDate()", "rupeng.com"); 占位符的方式不是字符串拼接,经过观察生成的 SQL 语句,发现仍然是参数化查询,因此不会 有 SQL 注入漏洞。 执行查询: var q1 = ctx.Database.SqlQuery<Item1>("select Name,Count(*) Count from T_Persons where Id>{0} and CreateDateTime<={1} group by Name", 2, DateTime.Now); //返回值是 DbRawSqlQuery<T> 类型,也是实现了 IEnumerable 接口 foreach(var item in q1) { Console.WriteLine(item.Name+":"+item.Count); } class Item1 { public string Name { get; set; } public int Count { get; set; } } 类似于 ExecuteScalar 的操作比较麻烦: int c = ctx.Database.SqlQuery<int>("select count(*) from T_Persons").SingleOrDefault();
4.EF对象状态管理
5、 EF 优化的一个技巧
如果查询出来的对象只是供显示使用,不会修改、删除后保存,那么可以使用
AsNoTracking()来使得查询出来的对象是 Detached 状态,这样对对象的修改也还是 Detached
状态,EF 不再跟踪这个对象状态的改变,能够提升性能。
var p1 = ctx.Persons.Where(p => p.Name == "rupeng.com").FirstOrDefault();
Console.WriteLine(ctx.Entry(p1).State);
改成:
var p1 = ctx.Persons.AsNoTracking().Where(p => p.Name == "rupeng.com").FirstOrDefault();
Console.WriteLine(ctx.Entry(p1).State);
因为 AsNoTracking()是 DbQuery 类(DbSet 的父类)的方法,所以要先在 DbSet 后调用
AsNoTracking()。
第七章:一对多,多对多配置
FluentAPI深入讲解
1、 HasMaxLength 设定字段的最大长度
public PersonConfig()
{
this.ToTable("T_Persons");
this.Property(p => p.Name).HasMaxLength(50);//长度为 50
}
2、 (有用)字段是否可空:
this.Property(p => p.Name).IsRequired() 属性不能为空;
this.Property(p => p.Name).IsOptional() 属性可以为空;(没用的鸡肋!)