c#6.0新语法回顾
#region 自动属性初始化(Auto-property initializers)
public string Name { get; set; } = "summit";
public int Age { get; set; } = 22;
public DateTime BirthDay { get; set; } = DateTime.Now.AddYears(-20);
public IList<int> AgeList
{
get;
set;
} = new List<int> { 10, 20, 30, 40, 50 };
#endregion
#region 字符串嵌入值(String interpolation)
Console.WriteLine($"年龄:{this.Age} 生日:{this.BirthDay.ToString("yyyy-MM-dd")}");
Console.WriteLine($"年龄:{{{this.Age}}} 生日:{{{this.BirthDay.ToString("yyyy -MM-dd")}}}");
Console.WriteLine($"{(this.Age <= 22 ? "小鲜肉" : "老鲜肉")}");
#endregion
#region 导入静态类(Using Static)
Console.WriteLine($"之前的使用方式: {Math.Pow(4, 2)}");
Console.WriteLine($"导入后可直接使用方法: {Pow(4, 2)}");
#endregion
#region 空值运算符(Null-conditional operators)
int? iValue = 10;
Console.WriteLine(iValue?.ToString());//不需要判断是否为空
string name = null;
Console.WriteLine(name?.ToString());
#endregion
#region 对象初始化器(Index Initializers)
IDictionary<int, string> dictOld = new Dictionary<int, string>()
{
{ 1,"first"},
{ 2,"second"}
};
IDictionary<int, string> dictNew = new Dictionary<int, string>()
{
[4] = "first",
[5] = "second"
};
#endregion
int exceptionValue = 10;
try
{
Int32.Parse("s");
}
catch (Exception e) when (exceptionValue > 1)//满足条件才进入catch
{
Console.WriteLine("catch");
//return;
}
#region nameof表达式 (nameof expressions) 可以防止硬编码
Console.WriteLine(nameof(peopleTest)); //获取peopleTest这个字符串
#endregion
c#7.0新语法回顾
#region out参数
{
this.DoNoting(out int x, out int y);
Console.WriteLine(x + y);
this.DoNoting(out var l, out var m);
}
/// <summary>
/// 具有模式的 IS 表达式
/// </summary>
/// <param name="o"></param>
public void PrintStars(object o)
{
if (o is null) return; // 常量模式 "null"
if (!(o is int i)) return; // 类型模式 定义了一个变量 "int i"
Console.WriteLine(new string('*', i));
}
/// <summary>
/// 可以设定任何类型的 Switch 语句(不只是原始类型)
/// 模式可以用在 case 语句中
/// Case 语句可以有特殊的条件
/// </summary>
/// <param name="text"></param>
private void Switch(string text)
{
int k = 100;
switch (text)
{
case "ElevenEleven" when k > 10:
Console.WriteLine("ElevenEleven");
break;
case "Eleven" when text.Length < 10:
Console.WriteLine("ElevenEleven");
break;
case string s when s.Length > 7://模式
Console.WriteLine(s);
break;
default:
Console.WriteLine("default");
break;
case null:
Console.WriteLine("null");
break;
}
}
元组
private (string, string, string) LookupName(long id) // tuple return type
{
return ("first", "middle", "last");
}
private (string first, string middle, string last) LookupNameByName(long id) // tuple return type
{
return ("first", "middle", "last");
//return (first: "first", middle: "middle", last: "last");
}
var result = this.LookupNameByName(1);
Console.WriteLine(result.first);
Console.WriteLine(result.middle);
Console.WriteLine(result.last);
Console.WriteLine(result.Item1);
Console.WriteLine(result.Item2);
Console.WriteLine(result.Item3);
#region 局部函数
{
Add(3);
int Add(int k)//闭合范围内的参数和局部变量在局部函数的内部是可用的,就如同它们在 lambda 表达式中一样。
{
return 3 + k;
}
}
core解析
原来的流程:asp.net–网站托管在IIS–IIS负责监听-转发请求–响应客户端
现在 :.net core–控制台–CreateWebHostBuilder(内置了KestrelServer)–启动了服务器–负责监听-转发请求–响应客户端
public static void Main(string[] args)
{
// build按照给定的配置初始化主机对象
// Run 以阻塞的方式运行,直到到我们关闭进程
// Start以非阻塞方式运行。
CreateHostBuilder(args).Build().Run();
Console.WriteLine("Web主机已运行");
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
// CreateDefaultBuilder创建默认主机生成器、
// 两种主机:通用主机、Web主机
// 1个区别:不提供HTTP服务,Web主机提供
// 把我们的内容根目录设置为 当前程序运行的目录
// 加载主机配置(环境变量、命令行参数)
// 加载应
// 创建默认通用主机生成器
Host.CreateDefaultBuilder(args)
// 重写和增强CreateDefaultBuilder定义的配置
// 增强的配置方法、
// ConfigureWebHostDefaults 干了哪些事情
// 加载前缀为“ASPNETCORE”的环境变量
// 将Kestrel设置为WEB服务器,并对其配置。
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureLogging((context, builder) => builder.SetMinimumLevel(LogLevel.Debug));
// 如果kestrel是运行IIS之后
webBuilder.ConfigureKestrel((context, options) => options.Limits.MaxRequestBodySize = null);
//use开头的是主机配置项:配置文件、命令行、硬编码、环境变量(launchSetting)
webBuilder.UseUrls("http://*:3000");
// 指定了Web应用到的启动类
webBuilder.UseStartup<Startup>();
});
Startup
ConfigureServices:配置服务
Configure:配置管道
ConfigureServices
//IServiceCollection–服务容器,ioc(控制反转)容器-》注册类型,请求,实现依赖注入
//主动找依赖对象-正转,被动获得**-反转,(所以对于依赖的控制转移给了第三方)
//依赖注入是一系列工具手段,为了松耦合可维护可测试,ioc容器就是那个第三方,一个升级版的自带物流的工厂
//IoC容器
// Add开头是内置的服务组件
// 添加了对控制器和API相关的功能,但是不支持视图和页面
//services.AddControllers();
添加了对控制器\视图和API相关的功能, ASP.NET Core 3.0 MVC 模板默认使用的
//services.AddControllersWithViews();
//services.AddRazorPages();
2.x
//services.AddMvc();
跨域
//services.AddCors();
EF Core\第三方日志框架\\
注册别人写好的,已经人家给你封装。
如果需要往容器里添加自己的服务就需要定接口和类-》IMessageService, EmailService
服务的三种生命周期/生存期,一个服务,只能有一个实现
单例,配置
//services.AddSingleton<IMessageService, EmailService>();
作用域
//services.AddScoped<IMessageService, EmailService>();
瞬时
//services.AddTransient<IMessageService, EmailService>();
如果是一个接口对应多个类,但上面的方法只能写一个,可以对注册行为再加一层封装
public class MessageServiceBuilder
{
public IServiceCollection ServiceCollection { get; set; }
public MessageServiceBuilder(IServiceCollection serviceCollection)
{
ServiceCollection = serviceCollection;
}
public void UseEmail()
{
ServiceCollection.AddScoped<IMessageService, EmailService>();
}
public void UseSms()
{
ServiceCollection.AddScoped<IMessageService, SmsService>();
}
}
// 扩展服务类
public static class MessageServiceExtensions
{
//扩展方法
public static void AddMessage(this IServiceCollection serviceCollection, Action<MessageServiceBuilder> configure)
{
var builder = new MessageServiceBuilder(serviceCollection);
configure(builder);
}
}
然后在Startup中
按约定封装服务
//services.AddMessage(builder =>
//{
// builder.UseEmail();
// builder.UseSms();;
//});
Configure
详情参考:https://www.cnblogs.com/JNLightGade/p/5737485.html
https://www.cnblogs.com/dotNETCoreSG/p/aspnetcore-3_1-application-startup.html
如何自己写一个中间件
首先对configure的第一个参数写一个扩展方法
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseTest(this IApplicationBuilder app)
{
return app.UseMiddleware<TestMiddleware>();
}
}
具体中间件构成
RequestDelegate,返回Task的InvokeAsync或invoke方法是必须的
public class TestMiddleware
{
private readonly RequestDelegate _next;
public TestMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IOptions<AppSetting> appOptions)
{
await httpContext.Response.WriteAsync($"path:{appOptions.Value.WebSetting.WebName}");
await _next(httpContext);
}
}
关于配置文件的设置和读取
生成主机时自动实例IConfiguration
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
//然后可以直接读取var title = _configuration["WebSetting:Title"];
但是这样在需要读取大量配置文件的时候很不方便
可以先创建与配置文件一一对应的modle类型,然后进行绑定读取
// 全部绑定
var appSetting = new AppSetting();
_configuration.Bind(appSetting);
// 部分绑定
var behavior = new Behavior();
_configuration.GetSection("Behavior").Bind(behavior);
但是绑定并不是全局的,可以使用注册服务的方式
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSetting>(_configuration);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions appOptions){
app.Run(async context =>
{
var connStr = appOptions.Value.ConnectionString;
});
}
路由
因为一开始不了解mvc的路由模式,这里比较晕
1、路由配置风格
集中式配置
前面章节提到的路由配置都是在Startup类中进行的集中式路由配置,集中配置的路由,除了template中没有配置{controller}参数,默认都是对所有控制器(Controller)生效的。这种集中配置的方式一般我们只要配置一个默认路由,其他情况我们只需要不满足默认模板的情况下进行配置即可。尤其是对URL没有友好度要求的应用,例如:后台管理系统
分散式配置/绑定式配置
对于集中式路由配置的方式,如果某个Controller/Action配置了特殊路由,对于代码阅读就会不太友好。不过没关系,ASP.NET Core MVC也提供了RouteAttribute可以让我们在Controller或者Action上直接指定路由模板。
不过要强调的是,一个控制器只能选择其中一种路由配置,如果控制器标记了RouteAttribute进行路由配置,那么集中式配置的路由将不对其生效。
2、绑定式路由配置
在项目Controllers目中新建TestController.cs继承与Controller
并配置Action与路由
详情访问:
https://ken.io/note/asp.net-core-tutorial-mvc-route(其实这一个系列都写的不错,对于看官方文档看吐的,这是一个救命稻草)
view
这里最关键的是学会RazorPage,这里不深究,注意事项后续补充
Signalr实时 web 功能
详细说明即使用:http://blog.rdiframework.net/article/225
blazor
一个mvvm模型的前端框架,官方文档:
gRpc
在gRPC中,客户端应用程序可以直接在不同机器上的服务器应用程序上调用方法,(并且不限制语言)。