ASP.NET Core学习笔记2
ASP.NET Core学习笔记2
table标签:table 表格
tfoot标签:table foot 表的脚注
thead标签:table head 定义表格的标题
th标签: table header cell 表头单元格
td标签: table data cell 表数据单元格
tr标签:table row 定义表格一行
<%=%>、<%%>、<%@%>、<%#%>的区别
1、<%= %> 嵌入C#表达式 |
里面放变量名,获取后台的变量值,直接输入变量到页面上,里面放的变量名,未经过encode eg: 后台: seession["ab"]=ab; 前台:<%= session["ab"] %> === 取值 <%:%> 里面放的变量名,经过encode <%=%> <% = expression %> 用于解析表达式 |
2、<% %> 嵌入C#代码
|
<%%>之间可以写服务器端代码,中间一般放函数或方法,典型的asp程序写法 <% %>嵌入式代码块是在呈现页面的过程中执行的服务器代码。 块中的代码可以执行编程语句,并调用当前页类中的函数。 eg: <% for(var i=0;i<10;i++) { } %> 后台:public string GetString(){} 前台:<% GetString(); %> |
3、<%@%> 表示引用 |
<%@...%>这个是页面指令,一般放在每个页面的最顶部,对页面的运行进行控制,如设置缓存,引用用户控件,导入命名空间 eg:<%@ Page Language="C#" %> |
4、<%# %> 数据绑定 |
服务器端控件的数据上下文绑定,只能用在数据绑定控件中 |
ASP.Net MVC中的@与<% %>
区别 |
@ |
<% %> |
视图引擎 |
Razor(cshtml) |
ASPX(C#) |
位置 |
@ 后面放置C#代码 |
<% %>之间放置C#代码 |
作用 |
嵌入C#代码,实现html和C#混编 |
嵌入C#代码,实现html和C#混编 |
优劣 |
写法简洁 |
写法复杂 |
布局视图
原因 |
大多数的Web应用程序网站通常包含以下部分:Header头部,Footer页脚,Menu导航菜单,View具体内容视图; 为了所有页面具有相同的布局,其中一个方法是每个页面视图都添加相同的元素,这将导致每个页面都具有很多重复性元素,最为严重的是,一旦修改布局,则要修改成百上千的视图页,维护工作量相当大; 另个一方法就是使用布局视图技术 ,该布局视图拥有通用的布局元素,具体的视图页中只需编写不同的显示代码即可,也就是说,布局视图相当于一个框架,对于不同的内容,均套在这个框架里面,从而保证所有视图的布局一致性;
|
代码 |
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> </head> <body> <div> @RenderBody() </div> </body> </html>
|
使用布局视图 |
布局视图的文件名和普通视图一样,都是cshtml; 使用布局视图: 方法一:在每个视图页中添加Layout属性; @{Layout=”~/Views/Shared/_Layout.cshtml”;ViewBag.Title=”Student Details”;} 方法二:在每个视图中使用渲染节点; @RenderSection(“Scripts”,required:false); @if(IsSection(“Scripts”)){@RenderSection(“Scripts”,required:false);}
|
布局视图优先级 |
_ViewStart.cshtml文件,支持分层,优先级如下: 1. 视图文件中的Layout属性; 2. 同文件夹中的_ViewStart.cshtml文件; 3. 父文件夹中的_ViewStart.cshtml文件; 4. 父父文件夹中的_ViewStart.cshtml文件; 这样,就可以实现,在不同的目中层次中的视图文件,使用不同的布局; 可以创建多个不同的视图文件,以适应不同的场合: _AdminLayout.cshtml _NonAdminLayout.cshtml 或者在同一个布局文件中,通过条件判断,以使用不同的布局; @{ if(User.IsInRole(“Admin”)){Layout=”_AdminLayout”;} else {Layout=”_NonAdminLayout”;} }
|
命名空间导入 |
_ViewImport.cshtml文件,向所有视图文件添加相同的命名空间; 该文件通常放在Views文件夹中;当然也可以放在各级子文件夹中; _ViewImport.cshtml文件同样支持分层功能,优先级顺序同_ViewStart.cshtml文件; @using MockSchoolManagement.Models @using MockSchoolManagement.ViewModels
|
支持的指令 |
_ViewImport.cshtml文件支持的@指令包括: 1) @using 2) @addTagHelper 3) @removeTagHelper 4) @tagHelperPrefix 5) @model 6) @inherits 7) @inject |
ASP.Net Core MVC中的路由
两种路由技术 |
常规路由和属性路由 |
||||||||
路由的定义 |
浏览器的请求到达应用程序时,MVC中的控制器会处理传入的HTTP请求,并响应用户操作; 请求的URL会被映射到控制器的操作方法上,这个映射过程就是由应用程序的路由规则来完成; |
||||||||
举例 |
http:://localhost:12345/Home/Index,其中的Home会映射到HomeController控制器类,而Index会映射到HomeController类中的Index()方法; Home/Details/1会映射到HomeController类中的Details()方法,方法的参数是1;也就是HomeController类→Details(id=1)
|
||||||||
默认路由 |
在Startup.cs文件中的Configure()方法中: public Config(IApplicationBuilder app,IHostingEnvironment env) { if(env.IsDevelopment()){app.UseDeveloperExceptionPage();} app.UseStaticFiles(); app.UseMvcWithDefaultRout(); } 其中的UseMvcWithDefaultRout()就将MVC和默认路由添加到了应用程序的请求处理管道中; 默认路由规则是{controller=Home}/{action=Index}/{id?} 使用默认路由的代码: public static IapplicationBuilder UseMvcDefaultRout(this IapplicationBuilder app) { if(app==null){throw new ArgumentNullException(nameof(app));} return app.UseMvc(routs=> { routs.MapRout(name:”default”,template:”{controller=Home}/{action=Index}/{id?}”;) }) } |
||||||||
默认路由举例 |
http://localhost:1234/Student/Details/1 这个映射过程称为模型绑定; id后面的?表示该参数可选;
{controller=Home}表示controller的默认值是Home; {action=Index}表示action的默认值是Index; {id?}表示id是一个可选参数; http://localhost:1234程序导航到应用程序根目录,则会使用默认的controller值和默认的action值;
|
||||||||
自定义路由 |
使用UseMvc()方法而不是使用UseMvcWithDefaultRout(); app.UseMvc(routes=> routes.MapRoute(“default”,”{controller=Home}/{action=Index}/{id?}”);) |
||||||||
属性路由 |
public class HomeController:Controller { [Route(“”)] [Route(“Home”)] [Route(“Home/Index”)] public ViewResult Index(){return View();} }
3种路由规则都会访问HomeController类中的Index()方法; |
||||||||
可选参数 |
public class HomeController:Controller { private IStudentRepository _studentRepository; //使用构造注入IStudentRepository public HomeController(IStudentRepository studentRepository) {_studentRepository=studentRepository;} //使用?表示id参数是可选参数,如果没有?,则id是必选参数 //?使得id参数可以为空 [Route(“Home/Details/{id?}”)] public IActionResult Details(int ? id) { HomeDetailsViewModel homeDetailsViewModel=new HomeDetailsViewModel //id??1表示,如果id不是空就使用id,如果id为空,就是用1 //相当于三元表达式id==null?1:id {Student=_studentRepository.GetStudent(id??1),PageTitle=”学生信息”;} return View(homeDetailsViewModel); } |
||||||||
名称问题 |
[Route(“WC”)] [Route(“NH/Index”)] public string Welcome(){return “hello world”;} 属性路由中的名称和控制器名称、操作方法名称之间没有强关联关系; |
||||||||
多层属性路由 |
可以在控制器和操作方法分别标识各自的路由,而无需在控制器中的每个操作方法中标识完整路径的路由信息; [Route(“Home/Index”)] [Route(“Home/Student”)] 在控制器上标识控制器的路由[Route(“Home”)] 而在两个操作方法上标识各自的Action路由即可,无需多次重复标识Home路由信息; [Route(“Index”)] [Route(“Student”)] 如果操作方法路由上以/或者~开头,则Controller路由不会与Action路由组合在一起; /或者~都表示从根目录开始查找该操作方法;
|
||||||||
动态属性路由 |
[Route(“[controller]”)]标注在控制器上,[controller]就会自动替换为该控制器的名称; [Route(“[action]”)]标注在操作方法上[action]就会自动替换为操作方法的名称; 方括号支持标记替换,如果修改了控制器或者操作方法的名称,就无需再次修改对应的Route;
|
||||||||
对比 |
|
服务注入的三种方式
AddSingleton |
AddSingleton():首次请求会创建Singleton服务,所有后续申请都会使用相同的实例; 每个程序只创建一次Singleton服务,并在整个应用程序的声明周期使用该实例; |
AddTransient |
AddTransient():每次请求都会创建一个实例; 每次请求都会提供一个新实例,无论是否在同一HTTP请求的范围内,还是跨越不同的HTTP请求; |
AddScoped |
AddScoped():在范围内的每个请求创建一个新的AddScoped实例; 不同的HTTP请求分别创建不同的实例; 同一个Web请求中的其他服务在调用这个请求的时候,都会使用该实例; AddScoped在一个客户端请求中是相同的,在多个客户端请求中是不同的; 在限定的HTTP请求范围内获得相同的实例,但是跨不同的HTTP请求将获得新实例; AddScoped在当前HTTP请求的整个生命周期内可用; |
模型绑定、模型验证
定义 |
模型绑定,就是将HTTP请求中的数据,映射到操作方法的对应参数上; MVC将请求的数据绑定到操作方法对应的参数中,这个过程就叫模型绑定; |
||||||||||||||
查找数据 |
模型绑定将按照下列顺序查找HTTP请求中的数据: Form Values 表单中的数据 Route Values路由中的值 Query Strings 查询字符串 |
||||||||||||||
验证属性 |
ASP.Net Core内置的模型验证属性
|
||||||||||||||
例子 |
[Display(Name=”名字”)] [Required(ErrorMessage=”请输入名字,不能为空”)] public string Name{get;set;} [Display(Name=”电子邮箱”)] [RegularExpression(@”^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+$”, ErrorMessage(“邮箱格式不正确”))] [Required(ErrorMessage(“请输入邮箱,不能为空”))] public string Email{get;set;} |
TageHelper
定义 |
TagHelper是服务器端的组件,他们在服务器上运行; TagHelper在Razor文件中创建和渲染HTML元素; TagHelper类似于HTML TagHelper; TagHelper可以提高生产效率,生成更加稳定、可靠、可维护的代码; |
导入 |
_ViewImports.cshtml文件中导入TagHelper @addTagHelper *,Microsoft.AdpNetCore.Mvc.TagHelpers *表示要导入MVC中的所有TagHelper; Microsoft.AdpNetCore.Mvc.TagHelpers是内置的TagHelper组件的位置; |
示例 |
<a href=”/home/details/@student.Id”>查看</a> @Html.ActionLink(“查看”,”details”,new{id=student.Id}) @Url.Action(“details”,”home”,new{id=student.Id}) <a asp-controller=”home”asp-action=”details” asp-route-id=”@student.Id”>查看</a>
|
优点 |
TagHelper是根据应用程序的路有模板自动生成的链接; 如果更改路由模板,则TagHelper会针对新的路有模板进行自动修改和适配; 路由模板修改后,TagHelper生成的链接依然能够正常工作; 如果硬编码超链接,当修改 路由模板时,必须在很多地方修改对应的超链接,这样很傻; |
Image |
Image TagHelper,服务器上的图片更改了就从服务器下载图片,否则就从本地缓存加载图片; <img src=”~images/noimage.png”asp-append-version=”true”/> Image TagHelper增强了img的标签属性,为静态图像文件提供了缓存清除行为,通过散列计算生成唯一的散列值并将其附加到img的URL中。 唯一的散列值会提示客户端从服务器重新加载图片,而不是从浏览器的缓存重新加载; 只有当服务器上的图片更改时,才会计算并缓存新的散列值; 如果图片未更改,则不会重新计算散列值,使用此散列值,浏览器会跟踪服务器上的图片内容是否已更改; Environment TagHelper,根据不同的环境呈现不同的内容; |
TagHelper |
Form TagHelper Label TagHelper Input TagHelper Select TagHelper Textarea TagHelper Validation TagHelper |
EntityFramework Core
EF Core |
EntityFramework Core简称为EF Core; EF Core是ORM框架,是轻量级的、可扩展的、开源的软件; EF Core是跨平台的,适用于Windows、maxOS、Linux; EF Core是微软官方推荐的数据访问平台; EF Core可以完成对数据库的增删改查这些琐事,从而节省大量时间; EF Core 就是应用程序代码和数据库之间的粘合剂,避免了在没有ORM情况下编写大量代码来访问数据库; |
||||||||
ORM |
ORM=Object Relational Mapper对象关系映射; ORM会帮助我们生成底层的SQL语句,让我们可以使用应用程序业务对象; ORM减少了开发人员编写的代码量; 如果不适用ORM,则需要很多访问数据库的代码或者SQL语句; ORM将数据库中的数据表映射称为类对象,这些类称为领域模型、实体类、模型类、领域类; 如无哦没有ORM,则要通过ADO.NET手动编写连接SQL数据库的类,这样做不但低效,而且很不安全,容易出现SQL注入漏洞问题; 比如增删改查等底层数据库表中的数据,必须在程序中编写代码,生成SQL语句; 当数据需要显式时,又需要编写自定义代码将数据库映射到模型;
|
||||||||
单层Web应用 |
只有一个Web库,通过这个库,以自建文件夹的形式将各个服务拆分开来; n Models文件夹用于存放实体; n DataRepositories用于存放仓储内容; n ViewModels用于存放视图模型; n Controller用于存放控制器; n Views用于存放控制器对应的视图; |
||||||||
三层架构 |
1. 界面层; 2. 业务逻辑层BLL; 3. 数据访问层DAL; 这些层都是单独的项目;
|
||||||||
四层架构 |
领域驱动架构DDD包含4个基本层: 展现层Presentation:向用户提供一个接口,和用户进行交互,也就是Web单层; 应用层Application:是展现层和领域层的中间者,协调业务对象去执行特定的任务,可以对 复杂的业务逻辑进行功能拼接; 领域等Domain:包括业务对象和业务规则,这是应用程序的核心层,用于存放领域实体及重要逻辑的实现; 基础设施层Infrastructure:提供通用上几乎来支持高层;基础设施层的仓储Repository可以通过ORM实现数据库的交互,或者提供发布邮件的支持;
上述4层通过创建类库(.NET Core类库)进行隔离;
|
||||||||
NuGet |
NuGet是一个开源软件包管理系统,原名NuPack; NuGet作为VS的扩展,能够简化在VS中添加、更新、删除库的操作; NuGet包被打包成ZIP压缩文件,文件扩展名.nupkg,使用开放打包约定OPC格式,包含编译代码DLL,与该代码相关的其他文件以及描述性清单; NuGet最大的优势就是,可以方便的进行程序包的管理、发布、升级; 如果将SQLServer作为程序的数据库,需要安装Microsoft.EntityFrameworkCore.SqlServer,这个NuGet包称为数据库提供程序包; 通过NuGet安装的包称为程序包或者NuGet包; 如果使用MySQL数据库,则需要安装Micorsoft.EntityFrameworkCore.MySql; 如果使用PostgreSQL数据库,则需要安装Micorsoft.EntityFrameworkCore.PostgreSQL;
|
||||||||
仓储模式 |
Repository表示存储库、仓储; 仓储模式是数据访问层的抽象呈现; 仓储模式隐藏了底层的数据源保存、查询的详细信息; 具体如何保存、查询数据的详细代码都在对应的仓储中; 仓储,用于保存、查询内存中集合的数据; 仓储,可以保存、查询数据库中的数据;(SQLServer、MySQL等) 可以建立一个基于Student类的仓储,完成基于学生信息的增删改查操作,而无需编写大量代码; |
||||||||
仓储接口 |
使用接口IStudentRepository来指定仓储模式,规则如下: 1. 仓储支持哪些操作(增删改查),也就是有哪些函数/方法; 2. 每个操作(函数/方法)所需的参数和返回值; 3. 仓储接口只包含它可以执行的操作,但是不包含具体的实现;(接口只声明函数,而不实现) 4. 具体的实现细节位于实现仓储接口的具体的实现类中; 简而言之,要明确接口有哪些成员函数、每个函数的参数和返回值是什么,接口中的函数只有声明而没有具体实现,具体实现要写在对应的实现类中; |
||||||||
优点 |
仓储模式的好处: 使代码更清晰、容易重用和维护; 有助于创建松耦合的系统; 如果要使用Oracle数据库,只需实现OracleRepository,然后再用依赖注入来注册OracleRepository即可; 单元测试项目中,容易使用模拟仓储代替实际仓储; 仓储模式不特定于数据库模式; |
||||||||
迁移 |
EF Core 迁移是指,将数据库架构和应用程序的领域模型(实体类)保持同步; 如果没有执行初始化迁移就运行项目,则会报错; 正确的做法:先创建迁移记录 ,通过执行该迁移来创建数据库; 使用迁移:可以使用程序包管理器控制台PMC或者NetCore命令行界面CLI; 迁移中的命令: get-help about_entityframeworkcore提供Entity Framework Core的帮助信息; Add-Migration添加迁移记录; Update-Datebase将数据库更新为指定的迁移; 组合使用get-help Add-Migration、get-help Update-Datebase; 规则:要先安装NuGet包Microsoft.EntityFrameworkCore.Tools,才能使用迁移命令; 迁移后,会自动创建一个Migration文件夹,文件夹中有一个InitialCreate.cs文件,该文件包含了创建相应数据库表所需的代码内容; 还有一个文件是AppDbContextModelSnapShot.cs文件; 执行迁移代码可以创建表;如果数据库尚不存在,则会先创建数据库,然后创建数据表; 使用Update-Database命令,可以更新数据库;
|
||||||||
播种 |
种子数据:EF Core可以为数据库中的表添加初始数据,这些数据也叫做种子数据; 播种:为数据库添加初始数据(种子数据)的过程叫做播种; 启用种子数据:在DbContext类中重写OnModelCreating()方法,并使用HasData()方法为Student实体播种数据; public class AppDbContext:DbContext {public AppDbContext(DbContextOptions<AppDbContext> options):base(options){} public DbSet<Student>Students{get;set;} protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.Entity<Student>().HasData(//这里的Student数据就是种子数据 new Student{Id=1,Name=”Tom”,Major=MajorEnum.Math,Email=”a@a.com”}); }} 将种子数据添加到数据库; 首先添加一条迁移记录Add-Migration SeedStudentsTable; 系统会自动生成迁移代码; 然后执行Update-Database命令,系统就会自动执行生成的代码,然后将数据迁移到数据库中; protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.Entity<Student>().HasData(//这里的Student数据就是种子数据 new Student{Id=1,Name=”Tom”,Major=MajorEnum.Math,Email=”a@a.com”, new Student{Id=2,Name=”Jerry”,Major=MajorEnum.Computer,Email=”b@a.com”, new Student{Id=3,Name=”Jack”,Major=MajorEnum.Art,Email=”c@a.com”);} 如果更改了种子数据,那么重新添加前已记录,并重新更新数据库即可; l Add-Migration SeedStudentsTable l Update-Database 为了保持DbContext类干净,不臃肿,可以将种子数据从DbContext类移动到ModelBuilder类的扩展方法中: public static class ModelBuilderExtensions {//扩展方法必须放在静态类中,扩展方法也必须是静态方法 //扩展方法至少要有一个扩展类的实例参数,并且该参数前必须加this关键字; public static void Seed(this ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().HasData( new Student{Id=1,Name=”Tom”,Major=MajorEnum.Math,Email=”a@a.com”, new Student{Id=2,Name=”Jerry”,Major=MajorEnum.Computer,Email=”b@a.com”, new Student{Id=3,Name=”Jack”,Major=MajorEnum.Art,Email=”c@a.com” );}} 然后在DbContext 类的OnModelCreating方法中,只需一行代码就能搞定了: protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.Seed();} 当领域类(实体类)发生变更时(例如添加字段),不需要手动对数据库架构进行更改,而应该通过迁移功能,使数据库架构和领域类(实体类)保持同步; Add-Migration AddPhotoPathToStudents Update-Database 要删除迁移代码文件, 可以使用Remove-Migration命令; |
上传文件:
IformFile类 |
public interface IFormFile { string ContentType{get;}//获取上传文件的Content-Type标头 string ContentDisposition{get;}//获取上传文件的原始的Content-Disposition标头 IHeaderDictionary Headers{get;}//获取上传文件的HTTP消息头的字典信息 long Length{get;}//获取文件长度,以字节为单位 string Name{get;}//从Content-Disposition标头中获取字段名称 string FileName{get;}//从Content-Disposition标头中获取文件名 Stream OpendReadStream();//打开请求流以读取上传的文件 void CopyTo(Stream target);//将上传文件的内容复制粘贴到流 //异步的将上传文件复制粘贴到流 Task CopyToAsync(Stream target,CancellationToken cancellationToken=null); } 要支持文件上传,表单元素应设置为enctype=“multipart/form-data”; enctype属性,规定了在发送到服务器之前,应该如何对表单数据进行编码; application/x-www-form-urlencoded表示在发送前编码所有字符(默认); multipart/form-data不对字符进行编码;在使用包含文件上传空间的表单时,必须使用该值; text/plain空格转换为加号+,但是不对特殊字符进行编码;
|
|
<a asp-controller=”home” asp-action=”edit” asp-route-id=”@student.Id” class=”btn btn-primary m-1”>编辑</a> asp-controller帮助程序定位到指定的控制器(控制器类); asp-action帮助程序定位到指定的操作方法(方法/函数); asp-route-id可以在路由中进行参数传递;参数传递给指定的操作方法; |
ASP.NET Core
术语 |
相关实体:包含外键属性的实体,有时称为关系的子类; 主题实体:包含主键、备用密钥属性的实体,有时称为关系的父类; 外键:依赖实体中的属性,用于存储与实体相关的主键属性的值; 主体密钥:标识唯一主体实体的属性,可能是主键或备用密钥; 导航属性:在主体或从属实体上定义的属性,包含对相关实体的引用; 集合导航属性:一个导航属性,其中包含对多个实体的引用; 引用导航属性:保存对单个相关实体的引用的导航属性; 反向导航属性:讨论特定导航属性时,是指关系另一端的导航属性; |
架构
|
架构是顶层设计; 框架是面向编程或配置的半成品; 组件是从技术维度上的复用; 模块是从业务维度上的职责划分; 系统是相互协同可运行的实体;
|
异步编码 |
在ASP.NET Core和EF Core的项目中,推荐使用异步方法; 为什么不用同步方法? 因为Web服务器的可用线程是有限的,而在高负载的情况下,所有线程可能都被占用; 当所有线程都被占用时,服务器无法处理新的请求,直到线程释放; 使用同步编码,可能出现多个线程被占用而无法执行任何操作的情况,因为他们正在等待IO完成; 简单点说,就是我们会感觉到网站访问速度很慢; 如果使用异步方法? 在等待IO线程时,如果使用异步编码,服务器会进行资源协调,将部分资源释放出来,用于处理当前请求; Web服务器的可用线程总量是不变的,异步编码只是相当于一个“巧妇”,尽可能的协调资源而已; 异步编码在运行时,会增加少量开销,在低流量时,对性能的影响可以忽略不计,但是在高流量的情况下潜在性能提升很大;
异步方法要有async关键字、Task<T>返回值、await关键字; public async Task<IActionResult> Index() {return View(await _studentRepository.GetAllListAsync());} async关键字告诉编译器,该方法的主体将生成回调,并自动创建Task<IActionResult>返回对象; 返回类型Task<IActionResult>表示该方法的返回结果是IActionResult类型; await关键字,会使编译器将方法拆分成两个部分: 1、将异步启动的操作结束;2、当操作完成时,调用回调方法;
|
分页 |
前台分页:一次性查询数据库中所有记录,然后在每页中显示指定的记录; 后台分页:对数据库进行多次查询,每次只获得本页的数据; 实现分页需要3个属性:当前页、要显示的记录总数、每页显示的记录数;
|
分部视图 |
调用分部视图的3种方法: @await Html.PartialAsync(“_Pagination”) @Html.Partial(“_Pagination”) <partial name=“_Pagination”/>
|
SQL语句 |
FromSql()从ASP.NET Core 3.0开始,该方法被弃用; FromSqlRaw()使用纯字符串从SQL查询返回对象; FromSqlInterpolated()使用插值字符串语法从SQL查询返回对象; |
数据加载 |
EF Core数据加载的3种方式: l 预加载:从数据库中查询处关联数据,使用方便;缺点是会加载所有数据,包括不需要加载的部分; l 显示加载:通过使用DbContext.Entry()显式调用类,以加载数据,对于集合类型数据使用Collection()方法,对于单个实体使用Reference()方法; l 延迟加载:表示在访问导航属性时才从数据库中加载关联数据; 预加载是EF Core推荐的数据加载方式; 性能对比: 预加载:首次读取实体时查询相关数据,通常是单次查询; 显示加载:首次读取实体时不会加载数据,只有当他需要查询相关数据时,才会向数据库发送查询指令,通常会产生多次查询; 延迟加载:读取实体时不会查询相关数据,当首次访问导航属性时,会自动查询导航属性(外键)所需的数据;每次访问导航属性的时候都会向数据库发送查询命令; 预加载性能相对较好,因为是一次加载;如果数据很复杂的时候,预加载对应的所有数据生成的SQL语句比较复杂,甚至可能会导致数据库无法有效处理EF Core生成的SQL语句; public class Department { public int DepartmentID{get;set;} [Display(Name=”学院名称”)] [StringLength(50,MinimumLength=3)] public string Name{get;set;} [DataType(DataType.Currency)] [Column(TypeName=”money”)] [Display(Name=”预算”)] public decimal Budget{get;set;} [TimeStamp] public byte[] RowVersion{get;set;} [Display(Name=””)] public int?TeacherID{get;set;} public virtual Teacher Administrator{get;set;}
} 对于导航属性(外键属性、领域模型对象属性),需要添加virtual关键字,否则会异常: An error occurred while starting the application .InvalidOperation Exception:Navigation property’Course’on entity type ‘CourseAssignment’is not virtual;UseLazyLoadingProxies requires all entity types to be public,unsealed ,have virtual navigation properties,and have a public or protected constructor; |
继承方式 |
EF Core的3种继承方式: l TPH(Table Per Hierarchy):所有数据都放在同一个表内,但是使用辨别标志Discrimination的方式进行区分,即通过Discrimination和DiscriminationID来进行区分; l TPC(Table Per Concrete-Type):由具体类型的表来存放各自数据,而各自没有任何关联,继承的实体会包含基类中的所有属性; l TPT(Table Per Type):每个对象各自独立产生表,这样各个表之间就没有直接关联,要额外实现关联性才能产生关联,子实体通过实体ID关联DiscriminationID找到父类; TPH和TPC的性能比TPT好,因为TPT模式会导致复杂的连接查询; 截止到EF Core 3.1仅仅支持TPH继承; |
角色验证
或验证 |
用户应至少具有以下角色之一才能访问控制器或操作 [Authorize(Roles ="Admin, Super User")]
|
与验证 |
用户应同时具有这两个角色,才能访问控制器或操作 [Authorize(Roles ="Super User")] [Authorize(Roles ="Admin")] |
ASP 授权机制
基于角色的授权
|
public string? Roles { get; set; } // 角色,可以通过英文逗号将多个角色分隔开,从而形成一个列表 //有该角色,就可以访问 [Authorize(Roles = "Admin")] //有其中任何一个角色,就可以访问 [Authorize(Roles = "Developer,Tester")] [Authorize(Roles ="System,Admin")]//基于角色的授权 //同时有两个角色,才可以访问 [Authorize(Roles = "Developer")] [Authorize(Roles = "Tester")] |
基于声明的授权
|
options.AddPolicy("RankClaim", policy => policy.RequireClaim("Rank")); 该策略名称为RankClaim,要求用户具有声明Rank,具体Rank对应的值是多少,不关心,只要有这个声明就好了。 options.AddPolicy("RankClaimM3", policy => policy.RequireClaim("Rank", "M3")); 我们添加了两条策略:RankClaimP3和RankClaimM3,除了要求用户具有声明Rank外,还分别要求Rank的值为P3和M3。 options.AddPolicy("RankClaimP3OrM3", policy => policy.RequireClaim("Rank", "P3", "M3")); options.AddPolicy("RankClaimP3AndM3", policy => policy.RequireClaim("Rank", "P3").RequireClaim("Rank", "M3")); 策略RankClaimP3OrM3要求用户具有声明Rank,且值为P3或M3即可;而策略RankClaimP3AndM3要求用户具有声明Rank,且值必须同时包含P3和M3。 |
基于策略的授权
|
public string? Policy { get; set; } options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System").Build());//双角色 或验证 options.AddPolicy("SystemAndAdmin", policy => policy.RequireRole("Admin").RequireRole("System").Build());//双角色 与验证
|
身份认证方案 |
public string? AuthenticationSchemes { get; set; } // 身份认证方案,可以通过英文逗号将多个身份认证方案分隔开,从而形成一个列表 |
CRUD是指在做计算处理时的增加(Create)、读取(Read)、更新(Update)和删除(Delete)几个单词的首字母简写。
Swagger/OpenAPI
定义 |
Swagger是一个语言无关的规范,用于描述RESTful API; Swagger项目已经捐赠给OpenAPI计划,现在它被称为OpenAPI; Swagger是一个强大的开源工具,可以生成WebAPI的交付式文档,还可以对接口进行调试; ASP.NET Core中,不需手动编码解析OpenAPI规范,已经有开源的Swashbuckle.ASPNetCore包; |
Swashbuckle |
Swashbuckle.ASPNetCore包含3个主要组件: Swashbuckle.ASPNetCore.Swagger:将Swagger对象模型和中间件转换为SwaggerDocument对象,然后作为公开JSON; Swashbuckle.ASPNetCore.SwaggerGen:是一个Swagger生成器,可以将SwaggerDocument从路由、控制器、模型中直接生成对象;通常与Swagger中间件结合,以生成自动公开的Swagger JSON; Swashbuckle.ASPNetCore.SwaggerUI:是一个嵌入式多版本Swagger UI工具;可以解析Swagger JSON来构建丰富的、可定制的界面,以呈现Web API的功能;它还会包含一些内置的公共方法测试工具; |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律