• .NET框架之“小马过河”


    .NET框架之“小马过河”

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

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

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

    有些非常简单的Http服务器,我看到有些.NET开发居然也用Node.jsPython等语言,一问,他们会回答说“这种简单的东西,用.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 CoreEF 6,其中EF Core可以同时运行在.NET Framework.NET Core中,但EF 6只能在.NET Framework中运行。本文中只测试了EF CoreEF 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很“重”

    SignalRASP.NET推出的抽象式的Http协议双向通信框架。SignalR可以用相同的API,支持像长轮询、Server Sent EventsWebSocket的技术。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客户端,如jsAndroid等,其中当然(显然)也包括.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骚操作】,
    DotNet骚操作

  • 相关阅读:
    网络通信之 字节序转换原理与网络字节序、大端和小端模式
    [C/C++]大小端字节序转换程序
    面向对象和面向过程的区别
    编译libjpeg
    地形系统lod
    c/c++ 代码中使用sse指令集加速
    个人作品- 蘑菇大战
    个人作品- 几何战争
    Obj格式模型 读取
    各大引擎矩阵的矩阵存储方式 ----行矩阵 or 列矩阵
  • 原文地址:https://www.cnblogs.com/sdflysha/p/20190911-dotnet-not-heavy-at-all.html
Copyright © 2020-2023  润新知