一、Core
1,防止过度发布
①TryUpdateModelAsync
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var emptyStudent = new Student();
if (await TryUpdateModelAsync<Student>(emptyStudent, "student", s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
_context.Students.Add(emptyStudent);
await _context.SaveChangesAsync();
return RedirectToPage("./Index"); }
return null;
}
code
[BindProperty]
public Student Student { get; set; }
public async Task<IActionResult> OnPostAsync(int? id)
{
if (!ModelState.IsValid)
{
return Page();
} var studentToUpdate =await _context.Students.FindAsync(id);
if (await TryUpdateModelAsync<Student>(studentToUpdate, "student", s => s.LastName, s => s.EnrollmentDate))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
code2
仅更新TryUpdateModelAsync列出的值
在上述示例中:
- 第二个自变量 (
"student", // Prefix
) 是用于查找值的前缀。 该自变量不区分大小写。 - 已发布的表单值通过模型绑定转换为
Student
模型中的类型。
②通过属性名称匹配
using System; namespace ContosoUniversity.Models
{
public class StudentVM
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
}
StudentVM
[BindProperty]
public StudentVM StudentVM { get; set; } public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
} var entry = _context.Add(new Student());
entry.CurrentValues.SetValues(StudentVM);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
code
2,Main
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting; namespace aspnetcoreapp
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
} public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
Main
UseHttpSys:用于在 HTTP.sys 中托管应用
UseContentRoot:用于指定根内容目录
Build
和 Run
方法生成 IWebHost
对象,该对象托管应用并开始侦听 HTTP 请求
UseStartup:指定Startup
类
3,Startup
ConfigureServices
定义应用所使用的服务(如 ASP.NET Core MVC、Entity Framework Core 和Identity)(可选择)
Configure
定义请求管道的中间件(必须)
ConfigureServices在 Configure之前执行
UseMvc
扩展方法将路由中间件添加到请求管道,并将 MVC 配置为默认设置。
4,添加过滤器
①定义Middleware
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using WebApplication1.Models; namespace WebApplication1
{
public class RequestSetOptionsMiddleware
{
private readonly RequestDelegate _next;
private IOptions<AppOptions> _injectedOptions; public RequestSetOptionsMiddleware(
RequestDelegate next, IOptions<AppOptions> injectedOptions)
{
_next = next;
_injectedOptions = injectedOptions;
} public async Task Invoke(HttpContext httpContext)
{
Console.WriteLine("RequestSetOptionsMiddleware.Invoke"); var option = httpContext.Request.Query["option"]; if (!string.IsNullOrWhiteSpace(option))
{
_injectedOptions.Value.Option = WebUtility.HtmlEncode(option);
} await _next(httpContext);
}
}
}
RequestSetOptionsMiddleware
②实现IStartupFilter
using Microsoft.AspNetCore.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; namespace WebApplication1
{
public class RequestSetOptionsStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
builder.UseMiddleware<RequestSetOptionsMiddleware>();
next(builder);
};
}
}
}
RequestSetOptionsStartupFilter
③在 ConfigureServices
的服务容器中注册 IStartupFilter
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IStartupFilter, RequestSetOptionsStartupFilter>();
services.AddMvc();
}
Code
5,依赖注入
①注册案例
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase()
); // Add framework services.
services.AddMvc(); // Register application services.
services.AddScoped<ICharacterRepository, CharacterRepository>();
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();
}
6,中间件
①中间件排序
②Use、Run 和 Map
Run:不会调用next方法。后面的管道将不会执行
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.Run(async (context)=>{
await context.Response.WriteAsync("Map Test 1");
}); if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} //使用wwwroot
app.UseStaticFiles(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
}); }
Run
Use:如果中间件不调用next方法,会使管道短路。
Map:如果请求路径以给定路径开头,则执行分支。
public class Startup
{
private static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
} private static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
} public void Configure(IApplicationBuilder app)
{
app.Map("/map1", HandleMapTest1); app.Map("/map2", HandleMapTest2); app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}
Map
public class Startup
{
private static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
} public void Configure(IApplicationBuilder app)
{
app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
HandleBranch); app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}
MapWhen 基于给定谓词的结果创建请求管道分支
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a"
//...
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b"
//...
});
});
Map 支持嵌套
app.Map("/level1/level2", HandleMultiSeg);
Map 还可同时匹配多个段
③自定义Middleware
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks; namespace IocDemo
{
public class CustomMiddleware
{
private readonly RequestDelegate _next; public CustomMiddleware(RequestDelegate next)
{
_next=next;
}
public Task InvokeAsync(HttpContext context)
{
this._next.Invoke(context);
context.Response.WriteAsync("CustomMiddleware");
return Task.CompletedTask;
}
}
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomer(this IApplicationBuilder budiler)
{
return budiler.UseMiddleware<CustomMiddleware>();
} } }
CustomMiddleware
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; namespace IocDemo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} //使用自定义middleware
app.UseCustomer(); //使用wwwroot
app.UseStaticFiles(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
}); }
}
}
Startup
7,静态文件
①提供 Web 根目录外的文件
请求 http://<server_address>/StaticFiles/images/banner1.svg 提供 banner1.svg 文件
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles(); // For the wwwroot folder app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = "/StaticFiles"
});
}
②设置 HTTP 响应标头
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// Requires the following import:
// using Microsoft.AspNetCore.Http;
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=600");
}
});
}
③静态文件授权方法
新建个静态文件根文件夹
[Authorize]
public IActionResult BannerImage()
{
var file = Path.Combine(Directory.GetCurrentDirectory(),
"MyStaticFiles", "images", "banner1.svg"); return PhysicalFile(file, "image/svg+xml");
}
④默认提供文档
public void Configure(IApplicationBuilder app)
{
app.UseDefaultFiles();
app.UseStaticFiles();
}
要提供默认文件,必须在 UseStaticFiles
前调用 UseDefaultFiles
。 UseDefaultFiles
实际上用于重写 URL,不提供文件。 通过 UseStaticFiles
启用静态文件中间件来提供文件。
public void Configure(IApplicationBuilder app)
{
// Serve my app-specific default file, if present.
DefaultFilesOptions options = new DefaultFilesOptions();
options.DefaultFileNames.Clear();
options.DefaultFileNames.Add("mydefault.html");
app.UseDefaultFiles(options);
app.UseStaticFiles();
}
将默认文件名更改为 mydefault.html
8,路由
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
典型路由
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id:int}");
路由约束
①尾随句点 . 是可选: files/{filename}.{ext?}
②*全方位参数: blog/{*slug}
将匹配以 /blog
开头且其后带有任何值(将分配给 slug
路由值)的 URI
9,环境
ASP.NET Core 在应用程序启动时读取环境变量 ASPNETCORE_ENVIRONMENT(如果未设置
ASPNETCORE_ENVIRONMENT
,将默认为 Production
)
ASPNETCORE_ENVIRONMENT值:
- Development(开发)
- Production(生产)
- Staging(暂存)
10,配置和选项
nuget Microsoft.Extensions.Configuration
①读取json文件
nuget Microsoft.Extensions.Configuration.Json
using System;
using System.IO;
// Requires NuGet package
// Microsoft.Extensions.Configuration.Json
using Microsoft.Extensions.Configuration; public class Program
{
public static IConfiguration Configuration { get; set; } public static void Main(string[] args = null)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json"); Configuration = builder.Build(); Console.WriteLine($"option1 = {Configuration["Option1"]}");
Console.WriteLine($"option2 = {Configuration["option2"]}");
Console.WriteLine(
$"suboption1 = {Configuration["subsection:suboption1"]}");
Console.WriteLine(); Console.WriteLine("Wizards:");
Console.Write($"{Configuration["wizards::Name"]}, ");
Console.WriteLine($"age {Configuration["wizards::Age"]}");
Console.Write($"{Configuration["wizards::Name"]}, ");
Console.WriteLine($"age {Configuration["wizards::Age"]}");
Console.WriteLine(); Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
{
"option1": "value1_from_json",
"option2": , "subsection": {
"suboption1": "subvalue1_from_json"
},
"wizards": [
{
"Name": "Gandalf",
"Age": ""
},
{
"Name": "Harry",
"Age": ""
}
]
}
json文件内容
节点由冒号 : 分隔: Configuration["subsection:suboption1"]
获取数组值: Configuration["wizards:0:Name"]
②读取xml文件
nuget Microsoft.Extensions.Configuration.Xml
using System;
using Microsoft.Extensions.Configuration;
using System.IO; namespace ConsoleDemo
{
class Program
{
static void Main(string[] args)
{
var builder= new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddXmlFile("test.xml");
var configuration=builder.Build();
Console.WriteLine(configuration["wizard:Harry:age"]);
}
}
}
<wizards>
<wizard name="Gandalf">
<age>1000</age>
</wizard>
<wizard name="Harry">
<age>17</age>
</wizard>
</wizards>
xml文件内容
③json绑定到对象里面
nuget Microsoft.Extensions.Configuration.Binder
using System;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Collections.Generic; namespace ConsoleDemo
{
class Program
{
static void Main(string[] args)
{
var dic=new Dictionary<string,string>();
dic.Add("Person:Name","hunter");
dic.Add("Person:Age","10"); var builder=new ConfigurationBuilder()
.AddInMemoryCollection(dic);
IConfiguration configuration=builder.Build(); var person=new Person();
configuration.GetSection("Person").Bind(person);
Console.WriteLine(person.Name);
Console.WriteLine(person.Age); }
} public class Person{
public string Name{get;set;}
public int Age{get;set;}
}
}
④在razor视图获取mvc视图中使用configuration
@page
@model IndexModel @using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration <!DOCTYPE html>
<html lang="en">
<head>
<title>Index Page</title>
</head>
<body>
<h1>Access configuration in a Razor Pages page</h1>
<p>Configuration["key"]: @Configuration["key"]</p>
</body>
</html>
在 Razor 页面页中
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration <!DOCTYPE html>
<html lang="en">
<head>
<title>Index View</title>
</head>
<body>
<h1>Access configuration in an MVC view</h1>
<p>Configuration["key"]: @Configuration["key"]</p>
</body>
</html>
在 MVC 视图中
④注入option配置
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"Option":{
"option1":"value1_from_json",
"option2":-1
} }
appsettings.json
public class MyOption{
public string option1{get;set;}
public int option2{get;set;}
}
自定义MyOption类
services.Configure<MyOption>(Configuration.GetSection("Option"));
在ConfigureServices注册
private IOptions<MyOption> _option;
public BlogController(IOptions<MyOption> option)
{
_option=option;
_option.Value.option1+_option.Value.option2
}
构造注入并使用
11,日志
①创建日志
nuget Microsoft.Extensions.Logging
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
private readonly ILogger _logger; public TodoController(ITodoRepository todoRepository,
ILogger<TodoController> logger)
{
_todoRepository = todoRepository;
_logger = logger;
}
要创建日志,请先从依赖关系注入容器获取 ILogger 对象
public IActionResult GetById(string id)
{
_logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
var item = _todoRepository.Find(id);
if (item == null)
{
_logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
return NotFound();
}
return new ObjectResult(item);
}
然后在该记录器对象上调用日志记录方法
②事件ID
LoggingEvents自己定义例如:
public class LoggingEvents
{
public const int GenerateItems = ;
public const int ListItems = ;
public const int GetItem = ;
public const int InsertItem = ;
public const int UpdateItem = ;
public const int DeleteItem = ; public const int GetItemNotFound = ;
public const int UpdateItemNotFound = ;
}
LoggingEvents
public IActionResult GetById(string id)
{
_logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
var item = _todoRepository.Find(id);
if (item == null)
{
_logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
return NotFound();
}
return new ObjectResult(item);
}
使用案例
12,使用Sesstion
using Microsoft.AspNetCore.Http
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders; namespace MvcDemo
{
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
HostingEnvironment=env;
} public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment{get;} // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var physicalProvider= HostingEnvironment.ContentRootFileProvider;
services.AddSingleton<IFileProvider>(physicalProvider); services.AddSession(option=>{
option.IdleTimeout=TimeSpan.FromSeconds();
option.Cookie.HttpOnly=true;//指示客户端脚本是否可以访问cookie。
}); services.AddMvc();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseSession();//要在UseMvc之前调用 app.UseStaticFiles(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Startup
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo.Models;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Http; namespace MvcDemo.Controllers
{
public class SesstionController : Controller
{ public string Index(string str)
{
HttpContext.Session.SetString("str",str); return "sesstion已经设置";
} public string GetSession()
{
var val= HttpContext.Session.GetString("str");
return val;
} }
}
使用
13,使用po文件配置本地化
nuget OrchardCore.Localization.Core
①配置Startup
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders; namespace MvcDemo
{
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
HostingEnvironment=env;
} public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment{get;} // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var physicalProvider= HostingEnvironment.ContentRootFileProvider;
services.AddSingleton<IFileProvider>(physicalProvider); services.AddSession(option=>{
option.IdleTimeout=TimeSpan.FromSeconds();
option.Cookie.HttpOnly=true;//指示客户端脚本是否可以访问cookie。
}); services.AddLocalization(option=>{ });
services.AddMvc()
.AddViewLocalization();
services.AddPortableObjectLocalization(options=>{
options.ResourcesPath="Localization";
});
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-US"),
new CultureInfo("en"),
new CultureInfo("fr-FR"),
new CultureInfo("fr"),
new CultureInfo("zh-CHS")
}; options.DefaultRequestCulture = new RequestCulture("zh-CHS");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
}); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseSession();//要在UseMvc之前调用 app.UseStaticFiles(); app.UseRequestLocalization(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
配置Startup
②使应用内容可本地化
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo.Models;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using System.Globalization;
using Microsoft.AspNetCore.Localization; namespace MvcDemo.Controllers
{
public class LocalizationController : Controller
{ private readonly IStringLocalizer<LocalizationController> _localization; public LocalizationController(IStringLocalizer<LocalizationController> localization)
{
_localization=localization;
} public string Index()
{
return _localization["Localization"];
} public string Get()
{
return _localization["Test{0}",];
} }
}
LocalizationController
③视图本地化
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = "Home Page";
}
<p>@Localizer["Localization"]</p>
<p>@Localizer["Test{0}",]</p>
视图
④DataAnnotations本地化
⑤po文件
msgid "Localization"
msgstr "Localization" msgid "Home"
msgstr "Home" msgid "Test{0}"
msgstr "Test{0}"
en.po
msgid "Localization"
msgstr "本地化" msgid "Home"
msgstr "主页" msgid "Test{0}"
msgstr "测试{0}"
zh-CHS.po
msgid:键
msgstr:值
14,在 ASP.NET 管道中运行 OWIN 中间件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Owin;
using System.Text;
using System.IO;
using System.Globalization; namespace AspNetCoreOwin
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseOwin(pipeline=>{
pipeline(next=>OwinHello);
}); app.UseStaticFiles(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
} public Task OwinHello(IDictionary<string,object> environment)
{
string responseText="hello owin";
byte[] responseBytes=Encoding.UTF8.GetBytes(responseText);
var responseStream=(Stream)environment["owin.ResponseBody"];
var responseHeaders=(IDictionary<string,string[]>)environment["owin.ResponseHeaders"];
responseHeaders["Content-Length"]=new string[]{responseBytes.Length.ToString(CultureInfo.InvariantCulture)};
responseHeaders["Content-Type"]=new string[] {"text/plain"};
return responseStream.WriteAsync(responseBytes,,responseBytes.Length);
}
}
}
Startup
案例下载:https://pan.baidu.com/s/1FLfBuqsMuKnv7wSF3BSW4w
15,WebSockets
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.WebSockets;
using System.Net.WebSockets;
using Microsoft.AspNetCore.Http;
using System.Threading;
using System.Collections.Concurrent; namespace WebSocketsDemo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseDefaultFiles();
app.UseStaticFiles(); var webSocketOption=new WebSocketOptions()
{
KeepAliveInterval=TimeSpan.FromSeconds(),//向客户端发送“ping”帧的频率,以确保代理保持连接处于打开状态
ReceiveBufferSize=*//用于接收数据的缓冲区的大小
};
app.UseWebSockets(webSocketOption); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
}); app.Use(async (context,next)=>{
if(context.Request.Path=="/ws")
{
if(context.WebSockets.IsWebSocketRequest)//判断是否是websocket请求
{
//将TCP连接升级到WebSocket连接,并提供websocket对象
WebSocket webSocket=await context.WebSockets.AcceptWebSocketAsync();
WebSockets.TryAdd(webSocket,"");
await Echo(context,webSocket);
}
else
{
context.Response.StatusCode=;
}
}
else
{
await next();
}
});
} private async Task Echo(HttpContext context,WebSocket websocket)
{
var buffer=new byte[*];
WebSocketReceiveResult result=await websocket.ReceiveAsync(new ArraySegment<byte>(buffer),CancellationToken.None);
while(!result.CloseStatus.HasValue)
{
//广播
foreach(var ws in WebSockets)
{
if(!ws.Key.CloseStatus.HasValue)
{
await ws.Key.SendAsync(new ArraySegment<byte>(buffer,,buffer.Length),result.MessageType,result.EndOfMessage,CancellationToken.None);
}
else
{
string value;
WebSockets.TryRemove(ws.Key,out value);
}
//await ws.SendAsync(new ArraySegment<byte>(buffer,0,buffer.Length),result.MessageType,result.EndOfMessage,CancellationToken.None);
} result=await websocket.ReceiveAsync(new ArraySegment<byte>(buffer),CancellationToken.None);
}
await websocket.CloseAsync(result.CloseStatus.Value,result.CloseStatusDescription,CancellationToken.None);
}
//可以开一个线程去检测WebSocket是否掉线,掉线则从字典中删除
private static ConcurrentDictionary<WebSocket,string> WebSockets=new ConcurrentDictionary<WebSocket,string>();
}
}
Startup
<!doctype html>
<html lang="en">
<head>
<title>Title</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="lib/jquery/dist/jquery.min.js"></script>
</head>
<body> <input type="text" id="message"/><input type="button" id="send" value="发送"/> <div id="context"></div> <!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body> <script> var ws;
if("WebSocket" in window)
{
ws=new WebSocket("ws://localhost:5000/ws");
ws.onopen=function(){
alert("开始")
}
ws.onmessage=function(res){
$("#context").append(res.data+"<br/>")
}
ws.onclose=function(){
alert("退出")
}
}
else
{
alert("不支持websocket");
} $(function(){ $("#send").click(function(){
ws.send($("#message").val())
})
}) </script>
</html>
index.html
案例下载:https://pan.baidu.com/s/1C5CLrtD6Mr66oiM7sfEHaw
16,使用内存缓存
nuget Microsoft.Extensions.Caching.Memory
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching; namespace CacheDemo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
}); services.AddMemoryCache();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
} app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Startup
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using CacheDemo.Models;
using Microsoft.Extensions.Caching.Memory; namespace CacheDemo.Controllers
{
public class HomeController : Controller
{ private IMemoryCache _cache; public HomeController(IMemoryCache cache)
{
_cache=cache;
} public IActionResult Index()
{
DateTime date;
if(_cache.TryGetValue("date",out date))
{
ViewData["date"]=date;
}
else
{
ViewData["date"]="未设置缓存";
}
return View();
} public IActionResult Set()
{
_cache.Set<DateTime>("date",DateTime.Now);
return RedirectToAction("Index");
} }
}
HomeController
案例下载:https://pan.baidu.com/s/1DEpF-_HLlEQFWswPyb-WkA
二、EF
1,Include和ThenInclude
使上下文加载 Student.Enrollments
导航属性,并在每个注册中加载 Enrollment.Course
导航属性
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; } public ICollection<Enrollment> Enrollments { get; set; }
} public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; } public Course Course { get; set; }
public Student Student { get; set; }
}
public class Course
{
//主键
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; } public ICollection<Enrollment> Enrollments { get; set; }
}
实体
2,通过依赖关系注入注册上下文
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddMvc();
}
配置连接字符串
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;ConnectRetryCount=0;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
appsettings.json
ConnectRetryCount=0
来防止 SQLClient 挂起
3,种子数据
using ContosoUniversity.Models;
using System;
using System.Linq; namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
context.Database.EnsureCreated(); // Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
} var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
};
foreach (Student s in students)
{
context.Students.Add(s);
}
context.SaveChanges(); var courses = new Course[]
{
new Course{CourseID=,Title="Chemistry",Credits=},
new Course{CourseID=,Title="Microeconomics",Credits=},
new Course{CourseID=,Title="Macroeconomics",Credits=},
new Course{CourseID=,Title="Calculus",Credits=},
new Course{CourseID=,Title="Trigonometry",Credits=},
new Course{CourseID=,Title="Composition",Credits=},
new Course{CourseID=,Title="Literature",Credits=}
};
foreach (Course c in courses)
{
context.Courses.Add(c);
}
context.SaveChanges(); var enrollments = new Enrollment[]
{
new Enrollment{StudentID=,CourseID=,Grade=Grade.A},
new Enrollment{StudentID=,CourseID=,Grade=Grade.C},
new Enrollment{StudentID=,CourseID=,Grade=Grade.B},
new Enrollment{StudentID=,CourseID=,Grade=Grade.B},
new Enrollment{StudentID=,CourseID=,Grade=Grade.F},
new Enrollment{StudentID=,CourseID=,Grade=Grade.F},
new Enrollment{StudentID=,CourseID=},
new Enrollment{StudentID=,CourseID=},
new Enrollment{StudentID=,CourseID=,Grade=Grade.F},
new Enrollment{StudentID=,CourseID=,Grade=Grade.C},
new Enrollment{StudentID=,CourseID=},
new Enrollment{StudentID=,CourseID=,Grade=Grade.A},
};
foreach (Enrollment e in enrollments)
{
context.Enrollments.Add(e);
}
context.SaveChanges();
}
}
}
DbInitializer
// Unused usings removed
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using ContosoUniversity.Data; namespace ContosoUniversity
{
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args); using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<SchoolContext>();
DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
} host.Run();
} public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
Program.cs
第一次运行该应用时,会使用测试数据创建并填充数据库。 更新数据模型时:
- 删除数据库。
- 更新 seed 方法。
- 运行该应用,并创建新的种子数据库。
4,级联删除
modelBuilder.Entity<Department>()
.HasOne(d => d.Administrator)
.WithMany()
.OnDelete(DeleteBehavior.Restrict)
Code
5,组合PK
public class CourseAssignment
{
public int InstructorID { get; set; }
public int CourseID { get; set; }
public Instructor Instructor { get; set; }
public Course Course { get; set; }
}
Model
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CourseAssignment>()
.HasKey(c => new { c.CourseID, c.InstructorID });
}
SchoolContext
6,使用原始sql
①调用返回实体的查询
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
} string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
var department = await _context.Departments
.FromSql(query, id)
.Include(d => d.Administrator)
.AsNoTracking()
.SingleOrDefaultAsync(); if (department == null)
{
return NotFound();
} return View(department);
}
FromSql
②调用返回其他类型的查询
public async Task<ActionResult> About()
{
List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
var conn = _context.Database.GetDbConnection();
try
{
await conn.OpenAsync();
using (var command = conn.CreateCommand())
{
string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
+ "FROM Person "
+ "WHERE Discriminator = 'Student' "
+ "GROUP BY EnrollmentDate";
command.CommandText = query;
DbDataReader reader = await command.ExecuteReaderAsync(); if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(), StudentCount = reader.GetInt32() };
groups.Add(row);
}
}
reader.Dispose();
}
}
finally
{
conn.Close();
}
return View(groups);
}
Code
③调用更新查询
[HttpPost]
public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
{
if (multiplier != null)
{
ViewData["RowsAffected"] =
await _context.Database.ExecuteSqlCommandAsync(
"UPDATE Course SET Credits = Credits * {0}",
parameters: multiplier);
}
return View();
}
Code
三、Razor页面
1,路由
<a asp-action="Edit" asp-route-studentID="@item.ID">Edit</a>
<a href="/Students/Edit?studentID=6">Edit</a>
2,命令搭建基架
参考文档:https://docs.microsoft.com/zh-cn/aspnet/core/data/ef-rp/intro#add-scaffold-tooling
3,SelectList
using ContosoUniversity.Data;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System.Linq; namespace ContosoUniversity.Pages.Courses
{
public class DepartmentNamePageModel : PageModel
{
public SelectList DepartmentNameSL { get; set; } public void PopulateDepartmentsDropDownList(SchoolContext _context,
object selectedDepartment = null)
{
var departmentsQuery = from d in _context.Departments
orderby d.Name // Sort by name.
select d; DepartmentNameSL = new SelectList(departmentsQuery.AsNoTracking(),
"DepartmentID", "Name", selectedDepartment);
}
}
} <div class="form-group">
<label asp-for="Course.Department" class="control-label"></label>
<select asp-for="Course.DepartmentID" class="form-control"
asp-items="@Model.DepartmentNameSL">
<option value="">-- Select Department --</option>
</select>
<span asp-validation-for="Course.DepartmentID" class="text-danger" />
</div>
Code
四、MVC
1,模型绑定
①常用的验证属性
[CreditCard]
:验证属性是否具有信用卡格式。[Compare]
:验证某个模型中的两个属性是否匹配。[EmailAddress]
:验证属性是否具有电子邮件格式。[Phone]
:验证属性是否具有电话格式。[Range]
:验证属性值是否落在给定范围内。[RegularExpression]
:验证数据是否与指定的正则表达式匹配。[Required]
:将属性设置为必需属性。[StringLength]
:验证字符串属性是否最多具有给定的最大长度。[Url]
:验证属性是否具有 URL 格式。
2,视图
①Razor语法
<p>Last week this time: @(DateTime.Now - TimeSpan.FromDays(7))</p>
计算 @() 括号中的所有内容,并将其呈现到输出中
@{
var joe = new Person("Joe", 33);
} <p>Age@(joe.Age)</p>
使用显式表达式将文本与表达式结果串联起来
@Html.Raw("<span>Hello World</span>")
输出不进行编码,但呈现为 HTML 标记
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
<text>Name: @person.Name</text>
}
带分隔符的显式转换
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
@:Name: @person.Name
}
使用 @ 的显式行转换
@if (value % 2 == 0)
{
<p>The value was even.</p>
}
else if (value >= 1337)
{
<p>The value is large.</p>
}
else
{
<p>The value is odd and small.</p>
}
@if、else if、else
@switch (value)
{
case 1:
<p>The value is 1!</p>
break;
case 1337:
<p>Your number is 1337!</p>
break;
default:
<p>Your number wasn't 1 or 1337.</p>
break;
}
@switch
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
}
@for
@foreach (var person in people)
{
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
}
@foreach
@{ var i = 0; }
@while (i < people.Length)
{
var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p> i++;
}
@while
@{ var i = 0; }
@do
{
var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p> i++;
} while (i < people.Length);
@do while
@using (Html.BeginForm())
{
<div>
email:
<input type="email" id="Email" value="">
<button>Register</button>
</div>
}
复合语句 @using
@try
{
throw new InvalidOperationException("You did something invalid.");
}
catch (Exception ex)
{
<p>The exception message: @ex.Message</p>
}
finally
{
<p>The finally statement.</p>
}
@try、catch、finally
②@inherits 指令对视图继承的类提供完全控制
using Microsoft.AspNetCore.Mvc.Razor; public abstract class CustomRazorPage<TModel> : RazorPage<TModel>
{
public string CustomText { get; } = "Gardyloo! - A Scottish warning yelled from a window before dumping a slop bucket on the street below.";
}
自定义 Razor 页面类型
@inherits CustomRazorPage<TModel> <div>Custom text: @CustomText</div>
CustomText 显示在视图中
<div>Custom text: Gardyloo! - A Scottish warning yelled from a window before dumping a slop bucket on the street below.</div>
呈现
③@functions
指令允许 Razor 页面将 C# 代码块添加到视图中
@functions {
public string GetHello()
{
return "Hello";
}
} <div>From method: @GetHello()</div>
视图
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor; public class _Views_Home_Test_cshtml : RazorPage<dynamic>
{
// Functions placed between here
public string GetHello()
{
return "Hello";
}
// And here.
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
WriteLiteral("\r\n<div>From method: ");
Write(GetHello());
WriteLiteral("</div>\r\n");
}
#pragma warning restore 1998
生成的 Razor C# 类
④_ViewImports.cshtml导入共享指令
支持的指令:
@addTagHelper
@removeTagHelper
@tagHelperPrefix
@using
@model
@inherits
@inject
针对 ASP.NET Core MVC 应用的 _ViewImports.cshtml
文件通常放置在 Views
文件夹中
3,标记帮助程序
①@addTagHelper
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper MvcDemo2.TagHelpers.EmailTagHelper,MvcDemo2
@addTagHelper之后第一个参数:需要加载的标记帮助类(*表示所有)
第二个参数:标记帮助类所在的程序集
②简单的将 <email>hunter</email> 变成 <a>hunter</a>
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;
namespace MvcDemo2.TagHelpers
{
//EmailTagHelper 的根名称是 email,因此 <email> 标记将作为目标名称
public class EmailTagHelper:TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName="a";//用<a>标签替换<email>
} }
}
①添加EmailTagHelper
@using MvcDemo2
@using MvcDemo2.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper MvcDemo2.TagHelpers.EmailTagHelper,MvcDemo2
②修改_ViewImports.cshtml
这时,在页面上的 <email>hunter</email> 会变成<a>hunter</a>
③设置自结束标签 <email/>
[HtmlTargetElement("email",TagStructure=TagStructure.WithoutEndTag)]
public class EmailTagHelper:TagHelper
HtmlTargetElement
如果存在不是自结束标签,就会报错
④将 <email mail-to="hunter"></email> 变成 <a href="hunter@qq.com">hunter@qq.com</a>
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;
namespace MvcDemo2.TagHelpers
{
//EmailTagHelper 的根名称是 email,因此 <email> 标记将作为目标名称
public class EmailTagHelper:TagHelper
{
public const string EmailDomain="qq.com";
//可以通过<email mail-to =“...”/>传递
//标记帮助程序采用 Pascal 大小写格式的类和属性名将转换为各自相应的小写短横线格式
public string MailTo{get;set;}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName="a";//用<a>标签替换<email>
var address=MailTo+"@"+EmailDomain; //给标签添加属性
output.Attributes.SetAttribute("href",address);
//设置<email></email>里面的内容
output.Content.SetContent(address);
} }
}
EmailTagHelper
⑤将 <email>hunter</email> 变成<a href="hunter@qq.com">hunter@qq.com</a>
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;
namespace MvcDemo2.TagHelpers
{
//EmailTagHelper 的根名称是 email,因此 <email> 标记将作为目标名称
public class EmailTagHelper:TagHelper
{
public const string EmailDomain="qq.com";
//可以通过<email mail-to =“...”/>传递 public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName="a";//用<a>标签替换<email> var content=await output.GetChildContentAsync();//获取标签里的内容
var tagter=content.GetContent()+"@"+EmailDomain; //给标签添加属性
output.Attributes.SetAttribute("href",tagter);
//设置<email></email>里面的内容
output.Content.SetContent(tagter);
} }
}
EmailTagHelper
⑥将页面中的 <bold>bbbb</bold> 替换为 <strong>bbbb</strong>
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace MvcDemo2.TagHelpers
{
public class BoldTagHelper:TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.RemoveAll("bold");
output.PreContent.SetHtmlContent("<strong>");
output.PostContent.SetHtmlContent("</strong>");
} }
}
BoldTagHelper
③将model传入标记帮助程序
using System;
namespace MvcDemo2.Models
{
public class WebsiteContext
{
public Version Version{get;set;}
public int CopyrightYear{get;set;}
public bool Approved{get;set;}
public int TagsToShow{get;set;}
}
}
Model
using Microsoft.AspNetCore.Razor.TagHelpers;
using MvcDemo2.Models;
namespace MvcDemo2.TagHelpers
{ //使用<website-information />
public class WebsiteInformationTagHelper:TagHelper
{
public WebsiteContext Info{get;set;} public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName="section";
output.Content.SetHtmlContent(
$@"<ul><li><strong>版本:</strong> {Info.Version}</li>
<li><strong>版权 年:</strong> {Info.CopyrightYear}</li>
<li><strong>是否批准:</strong> {Info.Approved}</li>
<li><strong>显示的标签:</strong> {Info.TagsToShow}</li></ul>"
);
} }
}
WebsiteInformationTagHelper
<website-information info="new WebsiteContext(){
Version=new Version(,),
CopyrightYear=,
Approved=true,
TagsToShow=
}"></website-information>
Html
⑥.NET 类型和生成的 HTML 类型
.NET 类型 | 输入类型 |
---|---|
Bool | type=”checkbox” |
String | type=”text” |
DateTime | type=”datetime” |
Byte | type=”number” |
Int | type=”number” |
Single、Double | type=”number” |
⑦数据注解和生成的 HTML 类型
特性 | 输入类型 |
---|---|
[EmailAddress] | type=”email” |
[Url] | type=”url” |
[HiddenInput] | type=”hidden” |
[Phone] | type=”tel” |
[DataType(DataType.Password)] | type=”password” |
[DataType(DataType.Date)] | type=”date” |
[DataType(DataType.Time)] | type=”time” |
⑧验证标记帮助程序
<span asp-validation-for="Email"></span>
针对具有 asp-validation-summary 属性的 <div> 元素
asp-validation-summary | 显示的验证消息 |
---|---|
ValidationSummary.All | 属性和模型级别 |
ValidationSummary.ModelOnly | 模型 |
ValidationSummary.None | 无 |
实例:
@model RegisterViewModel
@{
ViewData["Title"] = "Home Page";
} <a bold>aaa</a>
<bold>bbbb</bold>
<!--Razor 知道 info 属性是一个类,而不是字符串,并且你想要编写 C# 代码。 编写任何非字符串标记帮助程序属性时,都不应使用 @@ 字符。--> <website-information info="new WebsiteContext(){
Version=new Version(,),
CopyrightYear=,
Approved=true,
TagsToShow=
}"></website-information> <form asp-action="Register" method="POST" role="form">
<legend>注册</legend> <div asp-validation-summary="ModelOnly"></div> <div class="form-group">
<input asp-for="Email" class="form-control" placeholder="Input field">
<span asp-validation-for="Email"></span>
</div> <div class="form-group">
<input asp-for="Password" class="form-control" placeholder="Input field">
<span asp-validation-for="Password"></span>
</div> <button type="submit" class="btn btn-primary">Submit</button>
</form>
Index.cshtml
[HttpPost]
public IActionResult Register(RegisterViewModel model)
{
return View("Index");
}
Controller
⑨选择标记帮助程序
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering; namespace MvcDemo2.Models
{
public class CountryViewModel
{
public string Country{get;set;}
public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
new SelectListItem(){Value="MX",Text="墨西哥"},
new SelectListItem(){Value="CA",Text="加拿大"},
new SelectListItem(){Value="US",Text="美国"}
};
}
}
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models; namespace MvcDemo2.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
var country=new CountryViewModel();
country.Country="US";
return View(country);
} [HttpPost]
public IActionResult Index(CountryViewModel model)
{
return View(model);
} }
}
HomeController
@model CountryViewModel <form method="POST" asp-action="Index"> <select asp-for="Country" asp-items="Model.Countries"></select> <input type="submit" value="提交" />
</form>
Index
⑩枚举绑定
public enum CountryEnum
{
[Display(Name = "墨西哥合众国")]
Mexico,
[Display(Name = "美国")]
USA,
Canada,
France,
Germany,
Spain
}
定义枚举
<select asp-for="Country" asp-items="Html.GetEnumSelectList<CountryEnum>()"></select>
⑩选项分组
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Rendering; namespace MvcDemo2.Models
{
public class CountryViewModel
{
public string Country{get;set;} public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
new SelectListItem(){Value="MX",Text="墨西哥",Group=new SelectListGroup(){Name="分组一"}},
new SelectListItem(){Value="CA",Text="加拿大",Group=new SelectListGroup(){Name="分组二"}},
new SelectListItem(){Value="US",Text="美国",Group=new SelectListGroup(){Name="分组三"}}
}; } }
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models; namespace MvcDemo2.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
var country=new CountryViewModel();
country.Country="US";
return View(country);
} [HttpPost]
public IActionResult Index(CountryViewModel model)
{
return View(model);
} }
}
HomeController
<select asp-for="Country" asp-items="Model.Countries"></select>
⑩多重选择
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Rendering; namespace MvcDemo2.Models
{
public class CountryViewModel
{
public IEnumerable<string> Countrys{get;set;} public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
new SelectListItem(){Value="MX",Text="墨西哥"},
new SelectListItem(){Value="CA",Text="加拿大"},
new SelectListItem(){Value="US",Text="美国"}
}; } }
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models; namespace MvcDemo2.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
var country=new CountryViewModel();
country.Countrys=new []{"US","MX"};
return View(country);
} [HttpPost]
public IActionResult Index(CountryViewModel model)
{
return View(model);
} }
}
HomeController
@model CountryViewModel <form method="POST" asp-action="Index"> <select asp-for="Countrys" asp-items="Model.Countries"></select> <input type="submit" value="提交" />
</form>
index
⑩无选定内容
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Rendering; namespace MvcDemo2.Models
{
public class CountryViewModel
{
public string Country{get;set;} public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
new SelectListItem(){Value="MX",Text="墨西哥"},
new SelectListItem(){Value="CA",Text="加拿大"},
new SelectListItem(){Value="US",Text="美国"}
}; } }
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models; namespace MvcDemo2.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
var country=new CountryViewModel();
return View(country);
} [HttpPost]
public IActionResult Index(CountryViewModel model)
{
return View(model);
} }
}
HomeController
@model CountryViewModel <form method="POST" asp-action="Index"> <select asp-for="Country" asp-items="Model.Countries">
<option value="">--none--</option>
</select> <input type="submit" value="提交" />
</form>
index
4,内置标记帮助程序
①asp-all-route-data
@{
var parms = new Dictionary<string, string>
{
{ "speakerId", "" },
{ "currentYear", "true" }
};
} <a asp-route="speakerevalscurrent"
asp-all-route-data="parms">Speaker Evaluations</a>
试图
<a href="/Speaker/EvaluationsCurrent?speakerId=11¤tYear=true">Speaker Evaluations</a>
前面的代码生成以下 HTML:
[Route("/Speaker/EvaluationsCurrent",
Name = "speakerevalscurrent")]
public IActionResult Evaluations(
int speakerId,
bool currentYear) => View();
Controller
②asp-fragment
可定义要追加到 URL 的 URL 片段
<a asp-controller="Speaker"
asp-action="Evaluations"
asp-fragment="SpeakerEvaluations">Speaker Evaluations</a>
试图
<a href="/Speaker/Evaluations#SpeakerEvaluations">Speaker Evaluations</a>
生成的 HTML:
③asp-area
设置相应路由的区域名称
<a asp-area="Blogs"
asp-controller="Home"
asp-action="AboutBlog">About Blog</a>
试图
<a href="/Blogs/Home/AboutBlog">About Blog</a>
生成的 HTML:
④asp-protocol
在URL 中指定协议(比如 https
)
<a asp-protocol="https"
asp-controller="Home"
asp-action="About">About</a>
试图
<a href="https://localhost/Home/About">About</a>
生成的 HTML:
⑤asp-host
在 URL 中指定主机名
<a asp-protocol="https"
asp-host="microsoft.com"
asp-controller="Home"
asp-action="About">About</a>
试图
<a href="https://microsoft.com/Home/About">About</a>
生成的 HTML:
⑥缓存标记帮助程序
<cache enabled="true">@DateTime.Now</cache>
属性expires-after:设置过期时间
<cache expires-after="@TimeSpan.FromSeconds(120)">
Current Time Inside Cache Tag Helper: @DateTime.Now
</cache>
属性expires-sliding:设置多久未被访问过期设置
<cache expires-sliding="@TimeSpan.FromSeconds(60)">
Current Time Inside Cache Tag Helper: @DateTime.Now
</cache>
⑦环境标记帮助程序
<environment include="Development">Development</environment>
<environment include="Staging">Staging</environment>
<environment include="Production">Production</environment>
不同的环境显示不同的标签
⑧图像标记帮助程序
<img src="~/images/1.jpg" asp-append-version="true"/>
asp-append-version:追加版本号
5,分部视图
@await Html.PartialAsync("ViewName")
@await Html.PartialAsync("ViewName.cshtml")
@await Html.PartialAsync("~/Views/Folder/ViewName.cshtml")
@await Html.PartialAsync("/Views/Folder/ViewName.cshtml")
@await Html.PartialAsync("../Account/LoginPartial.cshtml")
@model string
姓名:@Model
分布页面Person.cshtml
@await Html.PartialAsync("Person","Hunter")
调用
6,视图组件
①添加 ViewComponent 类
在根目录新建一个ViewComponents文件夹,建ViewComponent类放在此文件夹中
using MvcDemo2.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq; namespace MvcDemo2.ViewComponents
{
public class ProductListViewComponent:ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(bool ischecked,string name)
{
var list=new List<Product>(){
new Product(){IsChecked=true,Name="iphone x",Price=},
new Product(){IsChecked=false,Name="iphone 8p",Price=},
};
var data= list.Where(m=>m.IsChecked==ischecked&&m.Name.Contains(name));
return View(data);
} }
}
ProductListViewComponent
②创建视图组件 Razor 视图
创建 Views/Shared/Components 文件夹。 此文件夹 必须 命名为 Components
@model IEnumerable<Product> @foreach(var item in Model)
{
<p>@item.Name</p>
}
Default.cshtml
@await Component.InvokeAsync("ProductList",new {ischecked=true,name="iphone"})
视图页面调用
7,上传文件
①使用模型绑定上传小文件
using System.Collections.Generic;
using Microsoft.AspNetCore.Http; namespace MvcDemo2.Models
{
public class FileUpViewModel
{
public IEnumerable<IFormFile> files {get;set;}
public string name{get;set;}
}
}
FileUpViewModel
public IActionResult FileUp(FileUpViewModel model)
{
return View();
}
Controller
<form asp-action="FileUp" enctype="multipart/form-data" method="POST" role="form">
<legend>提交</legend> <input type="file" name="files" class="form-control" multiple/>
<input type="text" name="name" class="form-control"/> <button type="submit" class="btn btn-primary">Submit</button>
</form>
View
8,筛选器
每种筛选器类型都在筛选器管道中的不同阶段执行。
授权筛选器最先运行,用于确定是否已针对当前请求为当前用户授权。 如果请求未获授权,它们可以让管道短路。
资源筛选器是授权后最先处理请求的筛选器。 它们可以在筛选器管道的其余阶段运行之前以及管道的其余阶段完成之后运行代码。 出于性能方面的考虑,可以使用它们来实现缓存或以其他方式让筛选器管道短路。它们在模型绑定之前运行,所以可以影响模型绑定。
操作筛选器可以在调用单个操作方法之前和之后立即运行代码。 它们可用于处理传入某个操作的参数以及从该操作返回的结果。
异常筛选器用于在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。
结果筛选器可以在执行单个操作结果之前和之后立即运行代码。 仅当操作方法成功执行时,它们才会运行。对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。
下图展示了这些筛选器类型在筛选器管道中的交互方式:
①筛选器通过不同的接口定义支持同步和异步实现
using FiltersSample.Helper;
using Microsoft.AspNetCore.Mvc.Filters; namespace FiltersSample.Filters
{
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
} public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
}
}
}
同步实现
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters; namespace FiltersSample.Filters
{
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
// 在行动执行之前做一些事情
var resultContext = await next();
// 在执行操作后执行某些操作; resultContext.Result将被设置
}
}
}
异步实现
②添加为全局筛选器
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
"Result filter added to MvcOptions.Filters")); // an instance
options.Filters.Add(typeof(SampleActionFilter)); // by type
options.Filters.Add(new SampleGlobalActionFilter()); // an instance
}); services.AddScoped<AddHeaderFilterWithDi>();
}
ConfigureServices
③取消和设置短路
通过设置提供给筛选器方法的 context
参数上的 Result
属性,可以在筛选器管道的任意位置设置短路。 例如,以下资源筛选器将阻止执行管道的其余阶段
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; namespace FiltersSample.Filters
{
public class ShortCircuitingResourceFilterAttribute : Attribute,
IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.Result = new ContentResult()
{
Content = "Resource unavailable - header should not be set"
};
} public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
}
ShortCircuitingResourceFilterAttribute
9,绑定与压缩
[
{
"outputFileName": "wwwroot/css/site.min.css",
"inputFiles": [
"wwwroot/css/site.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
]
bundleconfig.json
①需要引用: <DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.6.362" />
②执行命令: dotnet bundle 会合并并压缩inputFiles里的文件到outputFileName
五、Model
1,数据注解
①DisplayFormat
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
②Column
[Column("FirstName")]
public string FirstMidName { get; set; }
实体属性表字段映射
2,DatabaseGenerated标记主键
六、配置