Dapr在VS2019 + .Net Core 3.1环境的调试

上一篇文章《Dapr在VS2019 + .Net Core 3.1环境的试用》,我们已经成功试用了Dapr,那么接下来,我们讲一下Dapr的调试。

开发环境:Visual Studio 2019 Comunity / .Net Core 3.1 / Dapr CLI 1.5 / Windows 10 企业版 19043.1415

如图所示,应用程序与Dapr是两个单独的进程,它们之间通过HTTP/gRPC在预先定义好的端口沟通。所以无法像普通的项目那样F5一键debug。

在网络上搜索了大半天,都找不到靠谱的调试方式。

找到一篇文章貌似可以调试:Dapr + .NET 实战(九)本地调试

但针对的是VS Code,而且还要修改大段的配置文件,对新手不友好。

也有文章说在VS Code安装一个Dapr扩展就行。

但我想在VS开发啊,毕竟我习惯GUI傻瓜式操作……

所幸,在B站找到一个微软MVP UP主分享的视频,感觉挺不错,在这里分享一下:

一起学习 Dapr 调试&部署

在这里用文字表述出来,并且进行小小的改动。

1,修改Program.cs的CreateHostBuilder方法:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder
#if DEBUG
                    .UseUrls("https://localhost:5004")//Debug 环境配合Dapr边车的设置
#endif
                    .UseStartup<Startup>();
                });

因为Dapr的SideCar边车要监听来自特定端口的信息,所以我们指定了程序Url。可以参见  ASP.NET Core中配置监听URLs的五种方式

2,修改Startup.cs的ConfigureServices方法:

        public void ConfigureServices(IServiceCollection services)
        {
#if DEBUG
            //Dapr run -a MemberService -G 50004 -H 30004
            Process[] processes = Process.GetProcessesByName("daprd");
            if (processes.Any(x => x.GetCommandLineArgs().Contains("MemberService")) == false)
            {
                //string userFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
                //string daprdPath = System.IO.Path.Combine(userFolder, ".dapr\\bin\\daprd.exe");
                //string args = " -app-id MemberService -app-port 5004 -dapr-grpc-port 50004 -dapr-http-port 30004";// -components-path D:\\Temp\\Dapr_Demo\\.dapr\\components
                //Process.Start(daprdPath, args);
                Process.Start("dapr", "run -a MemberService -p 5004 -G 50004 -H 30004 --app-ssl");
            }

            //Debug 环境,让程序连接特定的Dapr端口
            services.AddControllers().AddDapr(config =>
            {
                config.UseHttpEndpoint("http://localhost:30004");
                config.UseGrpcEndpoint("http://localhost:50004");
            });
#else
            services.AddControllers().AddDapr();
#endif
        }

原理很简单:

a. 检查有没有MemberService的daprd进程,如果没有,则运行dapr run命令,指定名字、监听端口、边车gRPC端口、边车Http端口,以及指定Dapr 调用应用程序时是否启用 https

最后一步,如果创建Member项目的时候勾选了Configure for HTTPS则必须要指定“--app-ssl”,否则就会出现 the server closed connection before returning the first response byte 错误

b. 设置Dapr组件使用指定的Http终结点和gRPC终结点

dapr run命令会在server创建两个进程,一个是dapr,一个是真正干活的daprd

被注释的几行代码就是up主原来的代码,直接运行了daprd。但我发现这样的话,dapr dashboard无法看到dapr app的运行状况,所以我更改了一下,仍然运行dapr run命令。

配合上一篇文章《Dapr在VS2019 + .Net Core 3.1环境的试用》,我们已经可以一键F5对Member进行调试了。

只要在MemberController里面的方法设置断点,则无论是Http请求还是被其他Dapr边车请求,都能够断点调试。

参考:

一起学习 Dapr 调试&部署

-------------------------------------------------------------

文中使用了一个扩展方法GetCommandLineArgs,这是.Net没有的,可以查阅:.NET/C# 获取一个正在运行的进程的命令行参数

以下是详细代码:

/// <summary>
    ///<see cref="Process"/> 类型提供扩展方法。
    /// </summary>
    public static class ProcessExtensions
    {
        /// <summary>
        /// 获取一个正在运行的进程的命令行参数。
        ///<see cref="Environment.GetCommandLineArgs"/> 一样,使用此方法获取的参数是包含应用程序路径的。
        /// 关于 <see cref="Environment.GetCommandLineArgs"/> 可参见:
        /// [.NET 命令行参数包含应用程序路径吗?](https://walterlv.com/post/when-will-the-command-line-args-contain-the-executable-path.html)
        /// </summary>
        /// <param name="process">一个正在运行的进程。</param>
        /// <returns>表示应用程序运行命令行参数的字符串。</returns>
        public static string GetCommandLineArgs(this Process process)
        {
            if (process is null) throw new ArgumentNullException(nameof(process));

            try
            {
                return GetCommandLineArgsCore();
            }
            catch (Win32Exception ex) when ((uint)ex.ErrorCode == 0x80004005)
            {
                // 没有对该进程的安全访问权限。
                return string.Empty;
            }
            catch (InvalidOperationException)
            {
                // 进程已退出。
                return string.Empty;
            }

            string GetCommandLineArgsCore()
            {
                using (var searcher = new ManagementObjectSearcher(
                    "SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id))
                using (var objects = searcher.Get())
                {
                    var @object = objects.Cast<ManagementBaseObject>().SingleOrDefault();
                    return @object?["CommandLine"]?.ToString() ?? "";
                }
            }
        }
    }
原文地址:https://www.cnblogs.com/AlvinLiang/p/15785285.html