您所在的位置: > 主页 > 江苏网视 > 科技 > 正文
.NET框架之“小马过河”来源: 日期:2020-03-29 06:19:14  阅读:-

    .NET框架之“小马过河”

    有许多流行的 .NET框架,大家都觉得挺“重”,认为很麻烦,重量级,不如其它“轻量级”框架,从而不愿意使用。面对形形色色的框架发愁,笔者也曾发愁。但我发现只要敢于尝试,这些框架都是“纸老虎”。就像“小马过河”一样,自己尝试一下,就会发现“原来河水既不像老牛说的那样浅,也不像松鼠说的那样深。”

    项目中的代码,都在 LINQPad6中运行并测试通过,也可以复制到VisualStudio中执行。

    做简单的 Http服务器很“重”

    有些非常简单的 Http服务器,我看到有些.NET开发居然也用Node.jsPython等语言,一问,他们会回答说“这种简单的东西,用.NET,太重了”。殊不知其实用.NET做起来,也很轻(甚至更轻):

    1. // 代码不需要引入任何第三方包

    2. var http = new HttpListener;

    3. http.Prefixes.Add("http://localhost:8080/");

    4. http.Start;


    5. while (true)

    6. {

    7. var ctx = await http.GetContext;

    8. using var writer = new StreamWriter(ctx.Response.OutputStream);

    9. writer.Write(DateTime.Now);

    10. }

    运行效果:

    可见,包括空行,仅10行代码即可完成一个简单的 HTTP服务器。

    使用 EntityFramework很“重”

    EntityFramework,简称EF,现在有两个版本,EFCoreEF6,其中EFCore可以同时运行在.NETFramework.NETCore中,但EF6只能在.NETFramework中运行。本文中只测试了EFCore,但EF6代码也一样简单。

    EntityFramework.NET下常用的数据访问框架,以代码简单、功能强大而著名。但不少人却嗤之以鼻、不以为意。询问时,回答说EntityFramework很“重”。

    这个“重”字,我理解为它可能占用内存高,或者它可能代码极其麻烦,配置不方便(像iBatis/Hibernate那样),真的这样吗?

    如图,假设我有一个 UserVoiceStatus表:

    下面,我们通过 EF将数据取出来:

    1. // 引用NuGet包:

    2. // Microsoft.EntityFrameworkCore.SqlServer

    3. void Main

    4. {

    5. var db = new MyDB(new DbContextOptionsBuilder

    6. .UseSqlServer(Util.GetPassword("ConnectionString"))

    7. .Options);

    8. db.UserVoiceStatus.Dump;

    9. }


    10. public class UserVoiceStatus

    11. {

    12. public byte Id { get; set; }

    13. public string Name { get; set; }

    14. }


    15. public class MyDB : DbContext

    16. {

    17. public MyDB(DbContextOptions options): base(options)

    18. {

    19. }


    20. public DbSet<UserVoiceStatus> UserVoiceStatus { get; set; }

    21. }

    执行效果如图:

    注意,如果使用 LINQPad,事情还能更简单,只要一行代码即可,效果完全一样:UserVoiceStatuses

    使用 ASP.NET MVC很“重”

    上文说到了如何做一个简单的 Http服务器,如果想复杂一点,初始化ASP.NET MVC也很简单,甚至只需要一个文件即可完成:

    1. void Main

    2. {

    3. WebHost

    4. .CreateDefaultBuilder

    5. .UseStartup<UserQuery>

    6. .UseUrls("https://localhost:55555")

    7. .Build

    8. .Run;

    9. }


    10. public void ConfigureServices(IServiceCollection services)

    11. {

    12. services.AddControllers;

    13. }


    14. public void Configure(IApplicationBuilder app)

    15. {

    16. app.UseRouting;

    17. app.UseEndpoints(endpoints =>

    18. {

    19. endpoints.MapControllerRoute(

    20. name: "default",

    21. pattern: "{controller}/{action}/{id?}",

    22. defaults: new { controller = "Home", action = "Index" });

    23. });

    24. }


    25. namespace Controllers

    26. {

    27. public class HomeController : Controller

    28. {

    29. public DateTime Index

    30. {

    31. return DateTime.Now;

    32. }

    33. }

    34. }

    麻雀虽小,五脏俱全,这么简短的几千代码中,可以使用 Https、包含了依赖注入,还能完整的路由功能,就构成了ASP.NET MVC的基本代码。运行效果如图:

    使用 WebSockets很“重”

    WebSockets是个流行的Http双向通信技术,以前在Node.js中很流行(用socket.io)。代码如下:

    1. async Task Main

    2. {

    3. await WebHost

    4. .CreateDefaultBuilder

    5. .UseStartup<UserQuery>

    6. .UseUrls("https://*:55555")

    7. .Build

    8. .RunAsync;

    9. }


    10. async Task Echo(HttpContext ctx, WebSocket webSocket, CancellationToken cancellationToken)

    11. {

    12. var buffer = new byte[4096];

    13. ValueWebSocketReceiveResult result = await webSocket.ReceiveAsync(buffer.AsMemory, cancellationToken);

    14. while (!result.EndOfMessage)

    15. {

    16. await webSocket.SendAsync(buffer.AsMemory(..result.Count), result.MessageType, result.EndOfMessage, cancellationToken);

    17. result = await webSocket.ReceiveAsync(buffer.AsMemory, cancellationToken);

    18. }

    19. await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "NA", cancellationToken);

    20. }


    21. public void ConfigureServices(IServiceCollection services)

    22. {

    23. }


    24. public void Configure(IApplicationBuilder app)

    25. {

    26. app.UseWebSockets;

    27. app.Use(async (ctx, next) =>

    28. {

    29. if (ctx.Request.Path == "/ws")

    30. {

    31. if (ctx.WebSockets.IsWebSocketRequest)

    32. {

    33. WebSocket webSocket = await ctx.WebSockets.AcceptWebSocketAsync;

    34. await Echo(ctx, webSocket, CancellationToken.None);

    35. return;

    36. }

    37. }

    38. await next;

    39. });

    40. app.Run(x => x.Response.WriteAsync("Please call /ws using WebSockets."));

    41. }

    该代码是个 Echo服务器,它会将客户端发过来和内容,按原因返回给客户端。然后,.NET也内置了WebSockets的客户端:可以高效地访问刚刚创建并运行的WebSockets服务器。

    1. using (var ws = new ClientWebSocket)

    2. {

    3. await ws.ConnectAsync(new Uri("wss://localhost:55555/ws"), CancellationToken.None);

    4. var completeEvent = new ManualResetEventSlim;

    5. var cts = new CancellationTokenSource;

    6. new Task( => SendMessage(ws, cts)).Start;


    7. var buffer = new byte[4096];

    8. do

    9. {

    10. var r = await ws.ReceiveAsync(buffer, cts.Token);

    11. $"[{Util.ElapsedTime}] Received {Encoding.UTF8.GetString(buffer, 0, r.Count)}".Dump;

    12. } while (ws.State != WebSocketState.Closed);

    13. }

    14. $"[{Util.ElapsedTime}] Closed.".Dump;


    15. async void SendMessage(WebSocket ws, CancellationTokenSource cts)

    16. {

    17. for (var i = 0; i <3; ++i)

    18. {

    19. await ws.SendAsync(

    20. Encoding.UTF8.GetBytes($"[{Util.ElapsedTime}] Send {DateTime.Now.ToString}".Dump),

    21. WebSocketMessageType.Text,

    22. endOfMessage: false, default);

    23. await Task.Delay(1000);

    24. }

    25. await ws.CloseAsync(WebSocketCloseStatus.Empty, , default);

    26. cts.Cancel;

    27. }

    最后,客户端与服务器双向通信效果如下:

    使用 SignalR很“重”

    SignalRASP.NET推出的抽象式的Http协议双向通信框架。SignalR可以用相同的API,支持像长轮询、ServerSentEventsWebSocket的技术。SignalR默认优先选择使用WebSocket以达到最高性能,如果客户端或服务器不支持,则会回退至其它稍慢的技术。

    SignalR客户端还支持几乎所有语言、所有平台。它是如此好用,几乎可以取代传统的请求/响应,成为新的Http开发模型。(事实上Blazor正在尝试这样做)

    SignalR最为令人震撼的,还是它非常简单的使用方式,而恰恰是这一点给人误会最深。它的服务端API,甚至比WebSocket还要简单清晰简单:

    1. async Task Main

    2. {

    3. await WebHost

    4. .CreateDefaultBuilder

    5. .UseStartup<UserQuery>

    6. .UseUrls("https://localhost:55555")

    7. .Build

    8. .RunAsync;

    9. }


    10. public void ConfigureServices(IServiceCollection services)

    11. {

    12. services.AddSignalR;

    13. }


    14. public void Configure(IApplicationBuilder app)

    15. {

    16. app.UseRouting;

    17. app.UseEndpoints(endpoints =>

    18. {

    19. endpoints.MapHub<Hubs.ChatHub>("/chat");

    20. });

    21. }


    22. namespace Hubs

    23. {

    24. public class ChatHub : Hub

    25. {

    26. public async Task Broadcast(string id, string text)

    27. {

    28. await Clients.All.SendAsync("Broadcast", id, text);

    29. }

    30. }

    31. }

    前文提到, SignalR提供了所有平台的SignalR客户端,如jsAndroid等,其中当然(显然)也包括.NET的。SignalR.NET客户端使用起来也非常简单:

    1. // 引入NuGet包:Microsoft.AspNetCore.SignalR.Client

    2. // 代码在LINQPad中运行

    3. var hub = new HubConnectionBuilder

    4. .WithUrl("https://localhost:55555/chat")

    5. .Build;


    6. hub.On("Broadcast", (string id, string msg) =>

    7. {

    8. Console.WriteLine($"{id}: {msg}");

    9. });


    10. new Label("姓名: ").Dump;

    11. var idBox = new TextBox(Guid.NewGuid.ToString).Dump;

    12. await hub.StartAsync;

    13. while (true)

    14. {

    15. var text = Console.ReadLine;

    16. if (text == "Q") break;

    17. await hub.SendAsync("Broadcast", idBox.Text, text);

    18. }

    这是一个非常简单的多人聊天室,运行效果如下:

    总结

    面对形形色色的框架发愁,笔者也曾发愁。但现在不了,什么框架拿过来,马上试试,也就几十秒钟的事。好用不好用,用用便知。

    那么读者,你的“小马过河”的故事是怎样的呢?

    (正文已结束)

    免责声明及提醒:此文内容为本网所转载企业宣传资讯,该相关信息仅为宣传及传递更多信息之目的,不代表本网站观点,文章真实性请浏览者慎重核实!任何投资加盟均有风险,提醒广大民众投资需谨慎!