有许多流行的. 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> UserVoiceStatus { get; set; }
- }
执行效果如图:
注意, 如果使用 LINQPad, 事情还能更简单, 只要一行代码即可, 效果完全一样:
UserVoiceStatuses
使用 ASP.NET MVC 很 "重"
上文说到了如何做一个简单的 Http 服务器, 如果想复杂一点, 初始化 ASP.NET MVC 也很简单, 甚至只需要一个文件即可完成:
- void Main()
- {
- webHost
- .CreateDefaultBuilder()
- .UseStartup<UserQuery>()
- .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<UserQuery>()
- .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<UserQuery>()
- .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<Hubs.ChatHub>("/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);
- }
这是一个非常简单的多人聊天室, 运行效果如下:
总结
面对形形色色的框架发愁, 笔者也曾发愁. 但现在不了, 什么框架拿过来, 马上试试, 也就十几秒钟的事. 好用不好用, 用用便知.
那么读者, 你的 "小马过河" 的故事是怎样的呢?
来源: https://www.cnblogs.com/sdflysha/p/20190911-dotnet-not-heavy-at-all.html