.NET框架之“小马过河”
.NET框架之“小马过河”
有许多流行的 .NET
框架,大家都觉得挺“重”,认为很麻烦,重量级,不如其它“轻量级”框架,从而不愿意使用。面对形形色色的框架发愁,笔者也曾发愁。但我发现只要敢于尝试,这些框架都是“纸老虎”。就像“小马过河”一样,自己尝试一下,就会发现“原来河水既不像老牛说的那样浅,也不像松鼠说的那样深。”
项目中的代码,都在 LINQPad 6
中运行并测试通过,也可以复制到 Visual Studio
中执行。
做简单的 Http
服务器很“重”
有些非常简单的 Http
服务器,我看到有些 .NET
开发居然也用 Node.js
、 Python
等语言,一问,他们会回答说“这种简单的东西,用 .NET
,太重了”。殊不知其实用 .NET
做起来,也很轻(甚至更轻):
// 代码不需要引入任何第三方包 var http = new HttpListener(); http.Prefixes.Add("http://localhost:8080/"); http.Start(); while (true) { var ctx = await http.GetContext(); using var writer = new StreamWriter(ctx.Response.OutputStream); writer.Write(DateTime.Now); }
运行效果:
可见,包括空行,仅10行代码即可完成一个简单的 HTTP
服务器。
使用 Entity Framework
很“重”
Entity Framework
,简称 EF
,现在有两个版本, EF Core
和 EF 6
,其中 EF Core
可以同时运行在 .NET Framework
和 .NET Core
中,但 EF 6
只能在 .NET Framework
中运行。本文中只测试了 EF Core
, 但 EF 6
代码也一样简单 。
Entity Framework
是 .NET
下常用的数据访问框架,以代码简单、功能强大而著名。但不少人却嗤之以鼻、不以为意。询问时,回答说 Entity Framework
很“重”。
这个“重”字,我理解为它 可能 占用内存高,或者它 可能 代码极其麻烦,配置不方便(像 iBatis
/ Hibernate
那样),真的这样吗?
如图,假设我有一个 UserVoiceStatus
表:
下面,我们通过 EF
将数据取出来:
// 引用NuGet包: // Microsoft.EntityFrameworkCore.SqlServer void Main() { var db = new MyDB(new DbContextOptionsBuilder() .UseSqlServer(Util.GetPassword("ConnectionString")) .Options); db.UserVoiceStatus.Dump(); } public class UserVoiceStatus { public byte Id { get; set; } public string Name { get; set; } } public class MyDB : DbContext { public MyDB(DbContextOptions options): base(options) { } public DbSet UserVoiceStatus { get; set; } }
执行效果如图:
注意,如果使用 LINQPad
,事情还能更简单,只要一行代码即可,效果完全一样:
UserVoiceStatuses
使用 ASP.NET MVC
很“重”
上文说到了如何做一个简单的 Http
服务器,如果想复杂一点,初始化 ASP.NET MVC
也很简单,甚至只需要一个文件即可完成:
void Main() { WebHost .CreateDefaultBuilder() .UseStartup() .UseUrls("https://localhost:55555") .Build() .Run(); } public void ConfigureServices(IServiceCollection services) { services.AddControllers(); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); }); } namespace Controllers { public class HomeController : Controller { public DateTime Index() { return DateTime.Now; } } }
麻雀虽小,五脏俱全,这么简短的几千代码中,可以使用 Https
、包含了依赖注入,还能完整的路由功能,就构成了 ASP.NET MVC
的基本代码。运行效果如图:
使用 WebSockets
很“重”
WebSockets
是个流行的 Http
双向通信技术,以前在 Node.js
中很流行(用 socket.io
)。代码如下:
async Task Main() { await WebHost .CreateDefaultBuilder() .UseStartup() .UseUrls("https://*:55555") .Build() .RunAsync(); } async Task Echo(HttpContext ctx, WebSocket webSocket, CancellationToken cancellationToken) { var buffer = new byte[4096]; ValueWebSocketReceiveResult result = await webSocket.ReceiveAsync(buffer.AsMemory(), cancellationToken); while (!result.EndOfMessage) { await webSocket.SendAsync(buffer.AsMemory(..result.Count), result.MessageType, result.EndOfMessage, cancellationToken); result = await webSocket.ReceiveAsync(buffer.AsMemory(), cancellationToken); } await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "NA", cancellationToken); } public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { app.UseWebSockets(); app.Use(async (ctx, next) => { if (ctx.Request.Path == "/ws") { if (ctx.WebSockets.IsWebSocketRequest) { WebSocket webSocket = await ctx.WebSockets.AcceptWebSocketAsync(); await Echo(ctx, webSocket, CancellationToken.None); return; } } await next(); }); app.Run(x => x.Response.WriteAsync("Please call /ws using WebSockets.")); }
该代码是个 Echo
服务器,它会将客户端发过来和内容,按原因返回给客户端。然后, .NET
也内置了 WebSockets
的客户端:可以高效地访问刚刚创建并运行的 WebSockets
服务器。
using (var ws = new ClientWebSocket()) { await ws.ConnectAsync(new Uri("wss://localhost:55555/ws"), CancellationToken.None); var completeEvent = new ManualResetEventSlim(); var cts = new CancellationTokenSource(); new Task(() => SendMessage(ws, cts)).Start(); var buffer = new byte[4096]; do { var r = await ws.ReceiveAsync(buffer, cts.Token); $"[{Util.ElapsedTime}] Received {Encoding.UTF8.GetString(buffer, 0, r.Count)}".Dump(); } while (ws.State != WebSocketState.Closed); } $"[{Util.ElapsedTime}] Closed.".Dump(); async void SendMessage(WebSocket ws, CancellationTokenSource cts) { for (var i = 0; i < 3; ++i) { await ws.SendAsync( Encoding.UTF8.GetBytes($"[{Util.ElapsedTime}] Send {DateTime.Now.ToString()}".Dump()), WebSocketMessageType.Text, endOfMessage: false, default); await Task.Delay(1000); } await ws.CloseAsync(WebSocketCloseStatus.Empty, null, default); cts.Cancel(); }
最后,客户端与服务器双向通信效果如下:
使用 SignalR
很“重”
SignalR
是 ASP.NET
推出的抽象式的 Http
协议双向通信框架。 SignalR
可以用相同的 API
,支持像长轮询、 Server Sent Events
和 WebSocket
的技术。 SignalR
默认优先选择使用 WebSocket
以达到最高性能,如果客户端或服务器不支持,则会回退至其它稍慢的技术。
SignalR
客户端还支持几乎所有语言、所有平台。它是如此好用,几乎可以取代传统的请求/响应,成为新的 Http
开发模型。(事实上 Blazor
正在尝试这样做)
但 SignalR
最为令人震撼的,还是它非常简单的使用方式,而恰恰是这一点给人误会最深。它的服务端 API
,甚至比 WebSocket
还要简单清晰简单:
async Task Main() { await WebHost .CreateDefaultBuilder() .UseStartup() .UseUrls("https://localhost:55555") .Build() .RunAsync(); } public void ConfigureServices(IServiceCollection services) { services.AddSignalR(); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapHub("/chat"); }); } namespace Hubs { public class ChatHub : Hub { public async Task Broadcast(string id, string text) { await Clients.All.SendAsync("Broadcast", id, text); } } }
前文提到, SignalR
提供了所有平台的 SignalR
客户端,如 js
、 Android
等,其中当然(显然)也包括 .NET
的。 SignalR
的 .NET
客户端使用起来也非常简单:
// 引入NuGet包:Microsoft.AspNetCore.SignalR.Client // 代码在LINQPad中运行 var hub = new HubConnectionBuilder() .WithUrl("https://localhost:55555/chat") .Build(); hub.On("Broadcast", (string id, string msg) => { Console.WriteLine($"{id}: {msg}"); }); new Label("姓名: ").Dump(); var idBox = new TextBox(Guid.NewGuid().ToString()).Dump(); await hub.StartAsync(); while (true) { var text = Console.ReadLine(); if (text == "Q") break; await hub.SendAsync("Broadcast", idBox.Text, text); }
这是一个非常简单的多人聊天室,运行效果如下:
总结
面对形形色色的框架发愁,笔者也曾发愁。但现在不了,什么框架拿过来,马上试试,也就十几秒钟的事。好用不好用,用用便知。
那么读者,你的“小马过河”的故事是怎样的呢?
请关注我的微信公众号:【DotNet骚操作】,