一、Core

  1,防止过度发布

  2,Main

  3,Startup

  4,添加过滤器

  5,依赖注入

  6,中间件

  7,静态文件

  8,路由

  9,环境

  10,配置和选项

  11,日志

  12,使用Sesstion

  13,使用po文件配置本地化

  14,在 ASP.NET 管道中运行 OWIN 中间件

  15,WebSockets

  16,使用内存缓存

二、EF

  1,Include和ThenInclude

  2,通过依赖关系注入注册上下文

  3,种子数据

  4,级联删除

  5,组合PK

  6,使用原始sql

三、Razor页面

四、MVC

  1,模型绑定

  2,视图

  3,标记帮助程序

  4,内置标记帮助程序

  5,分部视图

  6,视图组件

  7,上传文件

  8,筛选器

  9,绑定与压缩

五、Model

六、配置

一、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,中间件

①中间件排序

Configure方法中定义的中间的顺序决定请求的顺序,响应的顺序则相反

②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[&quot;key&quot;]: @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[&quot;key&quot;]: @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&currentYear=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

outputFileName:要输出的捆绑文件名称。可以包含中的相对路径bundleconfig.json文件(必填)
inputFiles:要将捆绑在一起的文件的数组。 这些是配置文件的相对路径(可选)
minify:输出类型缩减选项
sourceMap:指示是否生成捆绑的文件的源映射的标志

①需要引用: <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标记主键

实体

六、配置

asp.net core2.0学习笔记的更多相关文章

  1. ASP.NET MVC2.0学习笔记:路由设置

    Route设置 在 <Professional in ASP.NET MVC2.0>一书的第四章,主要讲述了Route的简单设置.格式化设置.约束设置.区域路由.匹配文件.路由调试以及对R ...

  2. asp.net MVC2.0学习笔记

    asp.net;与mvc都是不可替代的:只是多一种选择:(解决了许多asp.net的许多缺点) model:充血模型.领域模型:很大程度的封装: 控制器:处理用户的交互,处理业务逻辑的调用,指定具体的 ...

  3. 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移

    不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx.superviso ...

  4. Asp.Net Core WebApi学习笔记(四)-- Middleware

    Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...

  5. ASP.NET Core 2 学习笔记(十三)Swagger

    Swagger也算是行之有年的API文件生成器,只要在API上使用C#的<summary />文件注解标签,就可以产生精美的线上文件,并且对RESTful API有良好的支持.不仅支持生成 ...

  6. ASP.NET Core 2 学习笔记(十二)REST-Like API

    Restful几乎已算是API设计的标准,通过HTTP Method区分新增(Create).查询(Read).修改(Update)和删除(Delete),简称CRUD四种数据存取方式,简约又直接的风 ...

  7. ASP.NET Core 2 学习笔记(十)视图

    ASP.NET Core MVC中的Views是负责网页显示,将数据一并渲染至UI包含HTML.CSS等.并能痛过Razor语法在*.cshtml中写渲染画面的程序逻辑.本篇将介绍ASP.NET Co ...

  8. sql server 关于表中只增标识问题 C# 实现自动化打开和关闭可执行文件(或 关闭停止与系统交互的可执行文件) ajaxfileupload插件上传图片功能,用MVC和aspx做后台各写了一个案例 将小写阿拉伯数字转换成大写的汉字, C# WinForm 中英文实现, 国际化实现的简单方法 ASP.NET Core 2 学习笔记(六)ASP.NET Core 2 学习笔记(三)

    sql server 关于表中只增标识问题   由于我们系统时间用的过长,数据量大,设计是采用自增ID 我们插入数据的时候把ID也写进去,我们可以采用 关闭和开启自增标识 没有关闭的时候 ,提示一下错 ...

  9. DirectX 总结和DirectX 9.0 学习笔记

    转自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html DirectX 总结 DDS DirectXDraw Surfac ...

随机推荐

  1. [转]bus error与segment error

    在c程序中,经常会遇到段错误(segment error)和总线错误(bus error),这两种问题出现的原因可能如下 段错误: 对一个NULL指针解引用. 访问程序进程以外的内存空间. 实际上,第 ...

  2. js中获取时间new date()的用法和获取时间戳

    获取时间: 1 var myDate = new Date();//获取系统当前时间 获取特定格式的时间: 1 myDate.getYear(); //获取当前年份(2位) 2 myDate.getF ...

  3. 泰克TDS1000B示波器使用说明

    1.前言 本文主要根据泰克官方网站TDS1000B/TDS2000B使用教程视频进行整理. 2.认识你的示波器 TDS1000B带宽从40MHZ到200MHZ,采样率高达2Gbps

  4. Linux命令:pigz多线程压缩工具【转】

    学习Linux系统时都会学习这么几个压缩工具:gzip.bzip2.zip.xz,以及相关的解压工具.关于这几个工具的使用和相互之间的压缩比以及压缩时间对比可以看:Linux中归档压缩工具学习 那么P ...

  5. ES系列十四、ES聚合分析(聚合分析简介、指标聚合、桶聚合)

    一.聚合分析简介 1. ES聚合分析是什么? 聚合分析是数据库中重要的功能特性,完成对一个查询的数据集中数据的聚合计算,如:找出某字段(或计算表达式的结果)的最大值.最小值,计算和.平均值等.ES作为 ...

  6. Salt Document学习笔记2

    配置文件需修改的内容及注意点: Edit the master config file: 1. Uncomment and change the user: root value to your ow ...

  7. jenkins jar包上传maven仓库

    1      Jenkins 编译后部署至 Maven 仓库 jenkins编译后构件(如:jar包)部署至maven仓库需修改以下内容:maven 仓库配置:项目 pom 文件:本地仓库的 sett ...

  8. aliyun添加数据盘后的物理分区和lvm逻辑卷两种挂载方式

    一.普通磁盘分区管理方式 1.对磁盘进行分区 列出磁盘 # fdisk -l # fdisk /dev/vdb Welcome to fdisk (util-linux 2.23.2). Change ...

  9. ES6的相关信息

    ECMAScript 是什么? ECMAScript 是 Javascript 语言的标准.ECMA European Computer Manufactures Association(欧洲计算机制 ...

  10. JProfiler 入门教程

    推荐文章:JProfiler 入门教程 一.安装JProfiler 从http://www.ej-technologies.com/下载5.1.2并申请试用序列号 二.主要功能简介 1.内存剖析 Me ...