[{"content":"Frp (fast reverse proxy) is an open-source tunneling tool: a server with a public IP runs frps, machines behind NAT run frpc, and the two keep a persistent connection. Traffic hitting ports on the public server is forwarded through that connection to internal services. Typical use case: exposing a web service or SSH on your home machine to the outside world.\nConfiguration files use TOML (frps.toml / frpc.toml; the INI format is deprecated since v0.52+).\nServer side (frps) Unpack the frp release on your public server and edit frps.toml:\nbindPort = 7000 # port frpc connects to auth.token = \u0026#34;a long random secret\u0026#34; # client must use the same value webServer.addr = \u0026#34;0.0.0.0\u0026#34; # built-in dashboard webServer.port = 7500 webServer.user = \u0026#34;admin\u0026#34; webServer.password = \u0026#34;dashboard password\u0026#34; vhostHTTPPort = 8080 # entry port for HTTP proxies, enable as needed Run it under systemd, /etc/systemd/system/frps.service:\n[Unit] Description = frp server After = network.target [Service] Type = simple ExecStart = /opt/frp/frps -c /opt/frp/frps.toml Restart = on-failure RestartSec = 5 [Install] WantedBy = multi-user.target sudo systemctl enable --now frps Remember to open ports 7000 (frpc connection), 7500 (dashboard), and any proxy ports in your cloud provider\u0026rsquo;s security group — this is the most common cause of \u0026ldquo;it doesn\u0026rsquo;t connect\u0026rdquo;.\nClient side (frpc) On the internal machine, edit frpc.toml:\nserverAddr = \u0026#34;server public IP\u0026#34; serverPort = 7000 auth.token = \u0026#34;same secret as the server\u0026#34; # Plain TCP: map internal port 22 to server port 6022 [[proxies]] name = \u0026#34;ssh\u0026#34; type = \u0026#34;tcp\u0026#34; localIP = \u0026#34;127.0.0.1\u0026#34; localPort = 22 remotePort = 6022 # HTTP site: served on vhostHTTPPort, routed by domain [[proxies]] name = \u0026#34;web\u0026#34; type = \u0026#34;http\u0026#34; localPort = 3000 customDomains = [\u0026#34;app.example.com\u0026#34;] Start it:\nfrpc -c frpc.toml Now ssh -p 6022 user@server-ip reaches the internal machine, and http://app.example.com:8080 opens internal port 3000 in a browser.\nOn Linux, run the client under systemd too (same unit file as above with frps replaced by frpc). On Windows, install with winget install fatedier.frp and schedule frpc -c frpc.toml to run at startup via Task Scheduler.\nTroubleshooting login to server failed: token in login doesn\u0026rsquo;t match token in configuration — the auth.token values differ, or the client is hitting a different frps instance. Port unreachable — check whether the proxy registered in frps.log first, then the cloud security group and system firewall (ufw / firewalld). HTTP proxy returns 404 — the customDomains must resolve to the server IP, and requests must hit vhostHTTPPort, not remotePort. Need HTTPS — put nginx in front of frps for TLS termination, or use frp\u0026rsquo;s https2http plugin. ","permalink":"/en/docs/frp/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/fatedier/frp\"\u003eFrp\u003c/a\u003e (fast reverse proxy) is an open-source tunneling tool: a server with a public IP runs \u003ccode\u003efrps\u003c/code\u003e, machines behind NAT run \u003ccode\u003efrpc\u003c/code\u003e, and the two keep a persistent connection. Traffic hitting ports on the public server is forwarded through that connection to internal services. Typical use case: exposing a web service or SSH on your home machine to the outside world.\u003c/p\u003e\n\u003cp\u003eConfiguration files use TOML (\u003ccode\u003efrps.toml\u003c/code\u003e / \u003ccode\u003efrpc.toml\u003c/code\u003e; the INI format is deprecated since v0.52+).\u003c/p\u003e","title":"Frp Tunneling: Server and Client Configuration"},{"content":"EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.\nThe EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable the editors to read the file format and adhere to the defined styles. EditorConfig files are easy to read and work well with version control systems.\nhttps://editorconfig.org\nVisual Studio 2019 and later support EditorConfig files, so you can use an EditorConfig file in Visual Studio to define and maintain code style settings.\nhttps://github.com/dotnet/aspnetcore/blob/main/.editorconfig\n// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the \u0026lt;\u0026lt;LICENSE_TYPE\u0026gt;\u0026gt; license. // See the LICENSE file in the project root for more information. Use an EditorConfig file in Visual Studio to add file header copyright information.\n// Copyright (c) HelloShop Corporation. All rights reserved. // Licensed under the MIT License. file_header_template = Copyright (c) HelloShop Corporation. All rights reserved.\\nLicensed under the MIT License. ","permalink":"/en/visualstudio/editorconfig/","summary":"\u003cp\u003eEditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.\u003c/p\u003e\n\u003cp\u003eThe EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable the editors to read the file format and adhere to the defined styles. EditorConfig files are easy to read and work well with version control systems.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://editorconfig.org\"\u003ehttps://editorconfig.org\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eVisual Studio 2019 and later support EditorConfig files, so you can use an EditorConfig file in Visual Studio to define and maintain code style settings.\u003c/p\u003e","title":"About the EditorConfig File"},{"content":"File Header Comments A file header comment is a comment used to state a file\u0026rsquo;s copyright and license. It usually includes the copyright notice, license information, author information, and other related details. File header comments are a good practice that helps developers understand the copyright and licensing information of a file.\nCopyright Declaration Convention // Copyright (c) HelloShop Corporation. All rights reserved. // See the license file in the project root for more information. namespace HelloShop.AppHost { internal class Class1 { } } About the EditorConfig Configuration File It maintains a consistent coding style across multiple developers working on the same project in different editors and IDEs.\nVisual Studio 2019 and later support EditorConfig files, so you can use an EditorConfig file in Visual Studio to define and maintain code style settings.\nCopyright Notice Template file_header_template = Copyright (c) HelloShop Corporation. All rights reserved.\\nSee the license file in the project root for more information. For the file name, you can use {fileName} as a placeholder.\n","permalink":"/en/helloshop/copyight/","summary":"\u003ch2 id=\"file-header-comments\"\u003eFile Header Comments\u003c/h2\u003e\n\u003cp\u003eA file header comment is a comment used to state a file\u0026rsquo;s copyright and license. It usually includes the copyright notice, license information, author information, and other related details. File header comments are a good practice that helps developers understand the copyright and licensing information of a file.\u003c/p\u003e\n\u003ch2 id=\"copyright-declaration-convention\"\u003eCopyright Declaration Convention\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Copyright (c) HelloShop Corporation. All rights reserved.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// See the license file in the project root for more information.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloShop.AppHost\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003einternal\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eClass1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"about-the-editorconfig-configuration-file\"\u003eAbout the EditorConfig Configuration File\u003c/h2\u003e\n\u003cp\u003eIt maintains a consistent coding style across multiple developers working on the same project in different editors and IDEs.\u003c/p\u003e","title":"Adding File Header Comments to the Project"},{"content":"Each microservice has its own OpenApi document, but in actual development, we prefer to aggregate the OpenApi documents of all microservices together for easy viewing and debugging. Microservices are all developed based on the Aspire framework, so we can use the service discovery function provided by the Aspire framework to automatically aggregate the OpenApi documents of all microservices.\nAutomatically configure OpenApi documentation using Aspire Service Discovery public class OpenApiConfigureOptions() : IConfigureOptions\u0026lt;SwaggerUIOptions\u0026gt; builder.Services.AddTransient\u0026lt;IConfigureOptions\u0026lt;SwaggerUIOptions\u0026gt;, OpenApiConfigureOptions\u0026gt;(); Customize OpenApi document styles Create the Custom.css file under the Resource/OpenApi folder.\n.swagger-ui .topbar-wrapper img { content: url(\u0026#39;https://test.com/logo.svg\u0026#39;); } .swagger-ui .topbar-wrapper .link::after { margin-left: 0.5rem; content: \u0026#34;HelloWorld\u0026#34;; } Associate CSS files to OpenApi documents public static class OpenApiExtensions { public static IServiceCollection AddOpenApi(this IServiceCollection) { services.Configure\u0026lt;SwaggerUIOptions\u0026gt;(options =\u0026gt; { options.DocumentTitle = Assembly.GetExecutingAssembly().GetName().Name; options.InjectStylesheet(\u0026#34;/ServiceDefaults/Resources/OpenApi/Custom.css\u0026#34;); }); } } public static IApplicationBuilder UseOpenApi(this IApplicationBuilder app) { // Configure the HTTP request pipeline. app.UseSwagger(apiConfigureOptions) app.Map(\u0026#34;/ServiceDefaults\u0026#34;, appBuilder =\u0026gt; appBuilder.UseStaticFiles(new StaticFileOptions { FileProvider = new EmbeddedFileProvider(Assembly.GetExecutingAssembly()) })) return app; } ","permalink":"/en/helloshop/open-api-aggregate/","summary":"\u003cp\u003eEach microservice has its own OpenApi document, but in actual development, we prefer to aggregate the OpenApi documents of all microservices together for easy viewing and debugging. Microservices are all developed based on the Aspire framework, so we can use the service discovery function provided by the Aspire framework to automatically aggregate the OpenApi documents of all microservices.\u003c/p\u003e\n\u003ch2 id=\"automatically-configure-openapi-documentation-using-aspire-service-discovery\"\u003eAutomatically configure OpenApi documentation using Aspire Service Discovery\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eOpenApiConfigureOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIConfigureOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eSwaggerUIOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddTransient\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIConfigureOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eSwaggerUIOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;,\u003c/span\u003e \u003cspan class=\"n\"\u003eOpenApiConfigureOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"customize-openapi-document-styles\"\u003eCustomize OpenApi document styles\u003c/h2\u003e\n\u003cp\u003eCreate the \u003ccode\u003eCustom.css\u003c/code\u003e file under the Resource/OpenApi folder.\u003c/p\u003e","title":"Aggregation OpenApi documentation"},{"content":"Use a gateway to aggregate multiple individual requests into a single request. This pattern is useful when a client must make multiple calls to different backend systems to perform an operation.\nContext and problem In some cases, a client needs to make multiple requests to multiple backend systems. For example, a client may need to retrieve data from multiple services and then aggregate that data into a single response. In this situation, the client must issue multiple requests, which can lead to performance problems. In addition, the client must also handle multiple responses, which can lead to complexity problems.\nSolution Use a gateway to reduce the frequency of communication between clients and services. The gateway receives client requests, dispatches the requests to different backend systems, then aggregates the results and returns them to the requesting client. This pattern can reduce the number of requests an application makes to backend services, and improve application performance over high-latency networks.\nPermission aggregation public interface IPermissionService { Task\u0026lt;IReadOnlyList\u0026lt;PermissionGroupDefinitionResponse\u0026gt;\u0026gt; GetAllPermissionDefinitionsAsync(CancellationToken cancellationToken=default); } Refactoring the users and roles controllers Change UsersController from IdentityServiceDbContext to UserManager\u0026lt;User\u0026gt;\nImplement RolesController using RoleManager\u0026lt;Role\u0026gt;\nChange UserCreateRequestValidator from IdentityServiceDbContext to UserManager\u0026lt;User\u0026gt;\nIn the JSON returned by the permission-definition endpoint, rename the name property to groupName\nFixing the remote permission check bug public async Task\u0026lt;IActionResult\u0026gt; CheckPermission(int roleId, string permissionName, string? resourceType = null, string? resourceId = null) Considerations The gateway should not introduce service coupling between backend services.\nThe gateway should be located close to the backend services to minimize latency.\nThe gateway service can become a single point of failure. Make sure the gateway is well designed to meet your application\u0026rsquo;s availability requirements.\nThe gateway can become a bottleneck. Make sure the gateway provides enough performance to handle the load and can scale as your usage grows.\nLoad test the gateway to ensure it doesn\u0026rsquo;t cause cascading failures in the services.\nImplement resilient design using techniques such as bulkheads, circuit breaking, retries, and timeouts.\nIf one or more service calls take too long, it may be acceptable to time out and return a partial data set. Consider how your application handles this situation.\nUse asynchronous I/O to ensure backend latency doesn\u0026rsquo;t cause performance problems in the application.\nImplement distributed tracing with correlation IDs to track each call.\nMonitor request metrics and response sizes.\nConsider returning cached data (as a failover strategy) to handle failures.\nInstead of building aggregation into the gateway, consider placing the aggregation service behind the gateway. The resource requirements for request aggregation may differ from those of other services in the gateway, and may affect the gateway\u0026rsquo;s routing and offloading capabilities.\n","permalink":"/en/helloshop/gateway-aggregation/","summary":"\u003cp\u003eUse a gateway to aggregate multiple individual requests into a single request. This pattern is useful when a client must make multiple calls to different backend systems to perform an operation.\u003c/p\u003e\n\u003ch2 id=\"context-and-problem\"\u003eContext and problem\u003c/h2\u003e\n\u003cp\u003eIn some cases, a client needs to make multiple requests to multiple backend systems. For example, a client may need to retrieve data from multiple services and then aggregate that data into a single response. In this situation, the client must issue multiple requests, which can lead to performance problems. In addition, the client must also handle multiple responses, which can lead to complexity problems.\u003c/p\u003e","title":"API Gateway Aggregation"},{"content":"Introduction JwtBearer token authentication is a JSON Web Token-based authentication method used to verify a user\u0026rsquo;s identity. It is a stateless authentication approach well suited to Web APIs and web applications.\nInstalling the NuGet package dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer Building your own Identity API endpoints builder.Services.AddIdentity\u0026lt;User, Role\u0026gt;(options =\u0026gt; { options.SignIn.RequireConfirmedAccount = false; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequiredLength = 5; }).AddEntityFrameworkStores\u0026lt;IdentityServiceDbContext\u0026gt;(); Validating tokens with JwtBearer builder.Services.AddAuthentication(options =\u0026gt; { options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultSignInScheme = CustomJwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options =\u0026gt; { options.TokenValidationParameters.ValidateIssuer = false; options.TokenValidationParameters.ValidateAudience = false; options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(issuerSigningKey)); }) A custom authentication handler public class CustomJwtBearerDefaults public class CustomJwtBearerOptions public class CustomJwtBearerHandler public class CustomJwtBearerExtensions Configuring token generation builder.Services.AddAuthentication().AddJwtBearer().AddCustomJwtBearer(options =\u0026gt; { options.IssuerSigningKey = issuerSigningKey; options.SecurityAlgorithm = SecurityAlgorithms.HmacSha256; }); Running the pgAdmin management tool in a container docker pull dpage/pgadmin4 docker run --name pgadmin -e PGADMIN_DEFAULT_EMAIL=test@test.com -e PGADMIN_DEFAULT_PASSWORD=test -e TZ=Asia/Shanghai -d -p 5050:80 dpage/pgadmin4 ","permalink":"/en/helloshop/jwt-bearer-authentication/","summary":"\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eJwtBearer token authentication is a JSON Web Token-based authentication method used to verify a user\u0026rsquo;s identity. It is a stateless authentication approach well suited to Web APIs and web applications.\u003c/p\u003e\n\u003ch2 id=\"installing-the-nuget-package\"\u003eInstalling the NuGet package\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Microsoft.AspNetCore.Authentication.JwtBearer\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"building-your-own-identity-api-endpoints\"\u003eBuilding your own Identity API endpoints\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddIdentity\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eRole\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireConfirmedAccount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireDigit\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireLowercase\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireUppercase\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireNonAlphanumeric\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequiredLength\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}).\u003c/span\u003e\u003cspan class=\"n\"\u003eAddEntityFrameworkStores\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"validating-tokens-with-jwtbearer\"\u003eValidating tokens with JwtBearer\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddAuthentication\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDefaultChallengeScheme\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eJwtBearerDefaults\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAuthenticationScheme\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDefaultAuthenticateScheme\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eJwtBearerDefaults\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAuthenticationScheme\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDefaultSignInScheme\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomJwtBearerDefaults\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAuthenticationScheme\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}).\u003c/span\u003e\u003cspan class=\"n\"\u003eAddJwtBearer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTokenValidationParameters\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValidateIssuer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTokenValidationParameters\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eValidateAudience\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTokenValidationParameters\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIssuerSigningKey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eSymmetricSecurityKey\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEncoding\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDefault\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetBytes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eissuerSigningKey\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"a-custom-authentication-handler\"\u003eA custom authentication handler\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomJwtBearerDefaults\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomJwtBearerOptions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomJwtBearerHandler\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomJwtBearerExtensions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"configuring-token-generation\"\u003eConfiguring token generation\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddAuthentication\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eAddJwtBearer\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eAddCustomJwtBearer\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIssuerSigningKey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eissuerSigningKey\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSecurityAlgorithm\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eSecurityAlgorithms\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHmacSha256\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"running-the-pgadmin-management-tool-in-a-container\"\u003eRunning the pgAdmin management tool in a container\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker pull dpage/pgadmin4\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run --name pgadmin -e \u003cspan class=\"nv\"\u003ePGADMIN_DEFAULT_EMAIL\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003etest@test.com -e \u003cspan class=\"nv\"\u003ePGADMIN_DEFAULT_PASSWORD\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"nb\"\u003etest\u003c/span\u003e -e \u003cspan class=\"nv\"\u003eTZ\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003eAsia/Shanghai -d -p 5050:80 dpage/pgadmin4\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Authenticating with JwtBearer Tokens"},{"content":"Automatically map entities and models using AutoMapper AutoMapper is an object mapping tool that can automatically map entity objects and model objects, reducing the workload of manual mapping and improving development efficiency. In addition, Mapster is also a high-performance object mapping tool that supports source generation code and has better performance. AutoMapper is more popular, more mature, more stable, easier to use, more flexible, more powerful, more comprehensive, and more popular than Mapster. AutoMapper is part of the .NET Foundation, an open source project, and a very excellent object mapping tool.\nInstall the NuGet package dotnet add package AutoMapper Register AutoMapper in dependency injection builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly()); Create mapping configuration file public class MappingProfile : Profile { public MappingProfile() { CreateMap\u0026lt;User, UserDto\u0026gt;(); CreateMap\u0026lt;UserDto, User\u0026gt;(); } } Mapping entities and models using AutoMapper public class UserService { private readonly IMapper _mapper; public UserService(IMapper mapper) { _mapper = mapper; } public UserDto GetUser(int id) { var user = _dbContext.Users.Find(id); return _mapper.Map\u0026lt;UserDto\u0026gt;(user); } public void UpdateUser(UserDto userDto) { var user = _mapper.Map\u0026lt;User\u0026gt;(userDto); _dbContext.Users.Update(user); _dbContext.SaveChanges(); } } Paging mode There are two common paging modes, one is based on page number and page size, and the other is based on cursor and page size.\n","permalink":"/en/helloshop/model-mapper/","summary":"\u003ch2 id=\"automatically-map-entities-and-models-using-automapper\"\u003eAutomatically map entities and models using AutoMapper\u003c/h2\u003e\n\u003cp\u003eAutoMapper is an object mapping tool that can automatically map entity objects and model objects, reducing the workload of manual mapping and improving development efficiency. In addition, Mapster is also a high-performance object mapping tool that supports source generation code and has better performance. AutoMapper is more popular, more mature, more stable, easier to use, more flexible, more powerful, more comprehensive, and more popular than Mapster. AutoMapper is part of the .NET Foundation, an open source project, and a very excellent object mapping tool.\u003c/p\u003e","title":"Automatic mapping of entities and models"},{"content":"Generating migration scripts dotnet ef migrations add InitialCreate --output-dir Infrastructure/Migrations Applying the latest migration to the database manually dotnet ef database update Creating the database with code await dbContext.Database.EnsureCreatedAsync(); EnsureCreatedAsync creates the database if it does not exist and creates all the tables, but it does not apply migration scripts. It is suitable for development environments.\nApplying the latest migration to the database with code await dbContext.Database.MigrateAsync(); MigrateAsync applies migration scripts and creates the database first if it does not exist. It is suitable for production environments.\nCaveats If the database was previously created with EnsureCreatedAsync, then calling MigrateAsync will throw an exception, because the database and all tables already exist and the migrations contain scripts that create those tables.\nIf the database does not exist, MigrateAsync will first create the database, then create the migration history table, and then apply the migration scripts. Along the way it prints some error and warning messages. They can be ignored, but it is not elegant.\nA more elegant approach This uses some EF Core internal services; note the differences in how they work.\nprivate static async ValueTask RunMigrationAsync(TDbContext dbContext, CancellationToken cancellationToken) { var strategy = dbContext.Database.CreateExecutionStrategy(); var dbCreator = dbContext.GetService\u0026lt;IRelationalDatabaseCreator\u0026gt;(); var historyRepository = dbContext.GetService\u0026lt;IHistoryRepository\u0026gt;(); await strategy.ExecuteAsync(async () =\u0026gt; { if (!await dbCreator.ExistsAsync(cancellationToken)) { await dbCreator.CreateAsync(cancellationToken); } await historyRepository.CreateIfNotExistsAsync(); await dbContext.Database.MigrateAsync(cancellationToken); }); } Use dbCreator.ExistsAsync to first check whether the database exists; if it does not, create only the database (it does not create tables). At this point you have an empty database.\nThen use historyRepository.CreateIfNotExistsAsync to create the migration history table in the empty database to hold the migration records.\nFinally, use dbContext.Database.MigrateAsync to apply the migration scripts. Because the scripts run against an empty database, they perform all operations including creating the tables.\nAfter migration completes, the database contains all the tables plus the migration history table, which records the latest migration.\nSubsequent calls to dbContext.Database.MigrateAsync will check the migration history table and apply any new migration scripts.\nThis achieves automatic migration and database initialization.\nTo run all of the above in a single transaction, we use dbContext.Database.CreateExecutionStrategy to create an execution strategy and perform the steps within that strategy.\nDatabase seeding approaches In the latest EF 9.0, the recommendation is to use UseSeeding and UseAsyncSeeding to seed the database with initial data; this code runs at migration time.\nprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)=\u0026gt; optionsBuilder .UseSqlServer(@\u0026#34;Server=(localdb)\\mssqllocaldb;Database=EFDataSeeding;Trusted_Connection=True;ConnectRetryCount=0\u0026#34;) .UseSeeding((context, _) =\u0026gt; { var testBlog = context.Set\u0026lt;Blog\u0026gt;().FirstOrDefault(b =\u0026gt; b.Url == \u0026#34;http://test.com\u0026#34;); if (testBlog == null) { context.Set\u0026lt;Blog\u0026gt;().Add(new Blog { Url = \u0026#34;http://test.com\u0026#34; }); context.SaveChanges(); } }) .UseAsyncSeeding(async (context, _, cancellationToken) =\u0026gt; { var testBlog = await context.Set\u0026lt;Blog\u0026gt;().FirstOrDefaultAsync(b =\u0026gt; b.Url == \u0026#34;http://test.com\u0026#34;, cancellationToken); if (testBlog == null) { context.Set\u0026lt;Blog\u0026gt;().Add(new Blog { Url = \u0026#34;http://test.com\u0026#34; }); await context.SaveChangesAsync(cancellationToken); } }); The traditional seed data approach is not recommended because it runs on every migration and does not support asynchronous operations.\nprotected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity\u0026lt;Blog\u0026gt;().HasData( new Blog { BlogId = 1, Url = \u0026#34;http://sample.com\u0026#34; }, new Blog { BlogId = 2, Url = \u0026#34;http://sample2.com\u0026#34; } ); } Database initialization in the Zero framework In some scenarios, database initialization can be a time-consuming operation, so we use an IHostedService or BackgroundService to run database initialization asynchronously.\npublic class DbInitializer : IHostedService { private readonly IServiceProvider _serviceProvider; public DbInitializer(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task StartAsync(CancellationToken cancellationToken) { using var scope = _serviceProvider.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService\u0026lt;AppDbContext\u0026gt;(); // The code to initialize the database } public Task StopAsync(CancellationToken cancellationToken) =\u0026gt; Task.CompletedTask; } ","permalink":"/en/helloshop/migration-dataseeding/","summary":"\u003ch2 id=\"generating-migration-scripts\"\u003eGenerating migration scripts\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet ef migrations add InitialCreate --output-dir Infrastructure/Migrations\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"applying-the-latest-migration-to-the-database-manually\"\u003eApplying the latest migration to the database manually\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet ef database update\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-the-database-with-code\"\u003eCreating the database with code\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003edbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDatabase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEnsureCreatedAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eEnsureCreatedAsync\u003c/code\u003e creates the database if it does not exist and creates all the tables, but it does not apply migration scripts. It is suitable for development environments.\u003c/p\u003e\n\u003ch2 id=\"applying-the-latest-migration-to-the-database-with-code\"\u003eApplying the latest migration to the database with code\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003edbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDatabase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMigrateAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eMigrateAsync\u003c/code\u003e applies migration scripts and creates the database first if it does not exist. It is suitable for production environments.\u003c/p\u003e","title":"Automatic Migration and Database Seeding"},{"content":"Model validation is an important feature in ASP.NET Core MVC that helps us verify that the data entered by users matches expectations.\nValidation with data annotations See the Microsoft data annotations documentation.\npublic class User { [Required] [StringLength(32)] public string Name { get; set; } [Required] [EmailAddress] public string Email { get; set; } } Validation with fluent chaining FluentValidation is a .NET library for building type-safe validation rules. Its design goal is to provide a simple, clear API while still supporting complex validation rules.\nSee the official FluentValidation documentation.\ndotnet add package FluentValidation.AspNetCore Implementing a validator public class UserValidator : AbstractValidator\u0026lt;User\u0026gt; { public UserValidator() { RuleFor(x =\u0026gt; x.Name).NotEmpty().MaximumLength(32); RuleFor(x =\u0026gt; x.Email).NotEmpty().EmailAddress(); } } Automatic dependency injection services.AddValidatorsFromAssembly(assembly).AddFluentValidationAutoValidation(); ValidatorOptions.Global.LanguageManager = new CustomFluentValidationLanguageManager(); Custom validation error messages public class UserValidator : AbstractValidator\u0026lt;User\u0026gt; { public UserValidator() { RuleFor(x =\u0026gt; x.Name).NotEmpty().MaximumLength(32).WithMessage(\u0026#34;Name is required and must be less than 32 characters.\u0026#34;); RuleFor(x =\u0026gt; x.Email).NotEmpty().EmailAddress().WithMessage(\u0026#34;Email is required and must be a valid email address.\u0026#34;); } } Custom validation logic public class UserCreateRequestValidator : AbstractValidator\u0026lt;UserCreateRequest\u0026gt; { public UserCreateRequestValidator(IdentityDbContext context) { RuleFor(m =\u0026gt; m.UserName).NotNull().NotEmpty().Length(5, 20).Matches(\u0026#34;^[a-zA-Z]+$\u0026#34;); RuleFor(m =\u0026gt; m.PhoneNumber).NotNull().NotEmpty().Length(11).Matches(@\u0026#34;^1\\d{10}$\u0026#34;).Must((model, phoneNumber) =\u0026gt; { return !context.Users.Any(e =\u0026gt; e.PhoneNumber == phoneNumber); }); RuleFor(m =\u0026gt; m.Password).NotNull().NotEmpty().Length(5, 20); RuleFor(m =\u0026gt; m.Email).EmailAddress().Length(5, 50); } } Common error messages public class CustomFluentValidationLanguageManager : FluentValidation.Resources.LanguageManager { public CustomFluentValidationLanguageManager() { AddTranslation(\u0026#34;en\u0026#34;, \u0026#34;NotNullValidator\u0026#34;, \u0026#34;The {PropertyName} field is required.\u0026#34;); AddTranslation(\u0026#34;en\u0026#34;, \u0026#34;MaximumLengthValidator\u0026#34;, \u0026#34;The {PropertyName} field must be less than {MaxLength} characters.\u0026#34;); AddTranslation(\u0026#34;en\u0026#34;, \u0026#34;EmailAddressValidator\u0026#34;, \u0026#34;The {PropertyName} field must be a valid email address.\u0026#34;); } } ","permalink":"/en/helloshop/model-validations/","summary":"\u003cp\u003eModel validation is an important feature in ASP.NET Core MVC that helps us verify that the data entered by users matches expectations.\u003c/p\u003e\n\u003ch2 id=\"validation-with-data-annotations\"\u003eValidation with data annotations\u003c/h2\u003e\n\u003cp\u003eSee the Microsoft \u003ca href=\"https://learn.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0#built-in-attributes-1\"\u003edata annotations\u003c/a\u003e documentation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUser\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [Required]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [StringLength(32)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [Required]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [EmailAddress]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eEmail\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"validation-with-fluent-chaining\"\u003eValidation with fluent chaining\u003c/h2\u003e\n\u003cp\u003eFluentValidation is a .NET library for building type-safe validation rules. Its design goal is to provide a simple, clear API while still supporting complex validation rules.\u003c/p\u003e","title":"Automatic Model Validation"},{"content":"Providing a Permission Check Endpoint in the Identity Service [HttpHead] public async Task\u0026lt;IActionResult\u0026gt; CheckPermission(string permissionName, string? resourceType = null, string? resourceId = null { if (await permissionChecker.IsGrantedAsync(permissionName, resourceType, resourceId)) { return Ok(); } return Forbid(); } Remote Permission Checker Dictionary\u0026lt;string, string?\u0026gt; parameters = new() { [nameof(name)] = name, [nameof(resourceType)] = resourceType, [nameof(resourceId)] = resourceId }; string queryString = QueryHelpers.AddQueryString(string.Empty, parameters); HttpRequestMessage request = new(HttpMethod.Head, queryString); using HttpResponseMessage response = await httpClient.SendAsync(request); return response.IsSuccessStatusCode; Reimplementing the Permission Handler public class PermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler\u0026lt;OperationAuthorizationRequirement\u0026gt; { protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement) { if (context.Resource is IAuthorizationResource resource) { if (await permissionChecker.IsGrantedAsync(context.User, requirement.Name, resource.ResourceType, resource.ResourceId)) { context.Succeed(requirement); } else { context.Fail(); } return; } if (await permissionChecker.IsGrantedAsync(context.User, requirement.Name)) { context.Succeed(requirement); return; } context.Fail(); } } Invalidating the Cache for Testing await distributedCache.SetObjectAsync(cacheKey, new PermissionGrantCacheItem(isGranted), new DistributedCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.Now }); Readability Refactoring Regenerate the demo data and rename Name in authorization to PermissionName, which is more readable.\nResource Descriptor Refactoring public record struct ResourceInfo(string ResourceType, string ResourceId) : IAuthorizationResource { public override readonly string ToString() =\u0026gt; $\u0026#34;{ResourceType}:{ResourceId}\u0026#34;; } ","permalink":"/en/helloshop/resource-based-authorization/","summary":"\u003ch2 id=\"providing-a-permission-check-endpoint-in-the-identity-service\"\u003eProviding a Permission Check Endpoint in the Identity Service\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e[HttpHead]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIActionResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eCheckPermission\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003epermissionName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eresourceType\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eresourceId\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003epermissionChecker\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsGrantedAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epermissionName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresourceType\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresourceId\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOk\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eForbid\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"remote-permission-checker\"\u003eRemote Permission Checker\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eDictionary\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eparameters\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [nameof(name)]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [nameof(resourceType)]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eresourceType\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [nameof(resourceId)]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eresourceId\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003equeryString\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eQueryHelpers\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddQueryString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eparameters\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eHttpRequestMessage\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eHttpMethod\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHead\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003equeryString\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003eHttpResponseMessage\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ehttpClient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSendAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsSuccessStatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"reimplementing-the-permission-handler\"\u003eReimplementing the Permission Handler\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePermissionRequirementHandler\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIPermissionChecker\u003c/span\u003e \u003cspan class=\"n\"\u003epermissionChecker\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eAuthorizationHandler\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOperationAuthorizationRequirement\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprotected\u003c/span\u003e \u003cspan class=\"kd\"\u003eoverride\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eHandleRequirementAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eAuthorizationHandlerContext\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eOperationAuthorizationRequirement\u003c/span\u003e \u003cspan class=\"n\"\u003erequirement\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eResource\u003c/span\u003e \u003cspan class=\"k\"\u003eis\u003c/span\u003e \u003cspan class=\"n\"\u003eIAuthorizationResource\u003c/span\u003e \u003cspan class=\"n\"\u003eresource\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003epermissionChecker\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsGrantedAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erequirement\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresource\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eResourceType\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresource\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eResourceId\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSucceed\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequirement\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFail\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003epermissionChecker\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsGrantedAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erequirement\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSucceed\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequirement\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFail\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"invalidating-the-cache-for-testing\"\u003eInvalidating the Cache for Testing\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003edistributedCache\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetObjectAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecacheKey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003ePermissionGrantCacheItem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eisGranted\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDistributedCacheEntryOptions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAbsoluteExpiration\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"readability-refactoring\"\u003eReadability Refactoring\u003c/h2\u003e\n\u003cp\u003eRegenerate the demo data and rename \u003ccode\u003eName\u003c/code\u003e in authorization to \u003ccode\u003ePermissionName\u003c/code\u003e, which is more readable.\u003c/p\u003e","title":"Best Practices for Resource-Based Authorization"},{"content":"Validation with DataAnnotations public class Employee { [Required(ErrorMessage = \u0026#34;Name is required\u0026#34;)] public string Name { get; set; } } @page \u0026#34;/employee\u0026#34; @using System.ComponentModel.DataAnnotations @using Microsoft.AspNetCore.Components.Forms \u0026lt;h3\u0026gt;Employee Form\u0026lt;/h3\u0026gt; \u0026lt;EditForm Model=\u0026#34;@employee\u0026#34; OnValidSubmit=\u0026#34;@HandleValidSubmit\u0026#34;\u0026gt; \u0026lt;DataAnnotationsValidator /\u0026gt; \u0026lt;ValidationSummary /\u0026gt; \u0026lt;div class=\u0026#34;form-group\u0026#34;\u0026gt; \u0026lt;label for=\u0026#34;Name\u0026#34;\u0026gt;Name\u0026lt;/label\u0026gt; \u0026lt;InputText id=\u0026#34;Name\u0026#34; @bind-Value=\u0026#34;employee.Name\u0026#34; class=\u0026#34;form-control\u0026#34; /\u0026gt; \u0026lt;ValidationMessage For=\u0026#34;@(() =\u0026gt; employee.Name)\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button type=\u0026#34;submit\u0026#34; class=\u0026#34;btn btn-primary\u0026#34;\u0026gt;Submit\u0026lt;/button\u0026gt; \u0026lt;/EditForm\u0026gt; @code { private Employee employee = new Employee(); } Validation with FluentValidation dotnet add package Blazored.FluentValidation public class EmployeeValidator : AbstractValidator\u0026lt;Employee\u0026gt; { public EmployeeValidator() { RuleFor(x =\u0026gt; x.Name).NotEmpty().WithMessage(\u0026#34;Name is required\u0026#34;); } } @page \u0026#34;/employee\u0026#34; @using FluentValidation @using FluentValidation.Results \u0026lt;h3\u0026gt;Employee Form\u0026lt;/h3\u0026gt; \u0026lt;EditForm Model=\u0026#34;@employee\u0026#34; OnValidSubmit=\u0026#34;@HandleValidSubmit\u0026#34;\u0026gt; \u0026lt;FluentValidationValidator DisableAssemblyScanning=\u0026#34;@true\u0026#34; /\u0026gt; \u0026lt;ValidationSummary /\u0026gt; \u0026lt;div class=\u0026#34;form-group\u0026#34;\u0026gt; \u0026lt;label for=\u0026#34;Name\u0026#34;\u0026gt;Name\u0026lt;/label\u0026gt; \u0026lt;InputText id=\u0026#34;Name\u0026#34; @bind-Value=\u0026#34;employee.Name\u0026#34; class=\u0026#34;form-control\u0026#34; /\u0026gt; \u0026lt;ValidationMessage For=\u0026#34;@(() =\u0026gt; employee.Name)\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button type=\u0026#34;submit\u0026#34; class=\u0026#34;btn btn-primary\u0026#34;\u0026gt;Submit\u0026lt;/button\u0026gt; \u0026lt;/EditForm\u0026gt; Custom Validation ","permalink":"/en/helloshop/webapp/blazor-validation/","summary":"\u003ch2 id=\"validation-with-dataannotations\"\u003eValidation with DataAnnotations\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eEmployee\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [Required(ErrorMessage = \u0026#34;Name is required\u0026#34;)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e@page\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;/employee\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e@using\u003c/span\u003e \u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eComponentModel\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDataAnnotations\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e@using\u003c/span\u003e \u003cspan class=\"n\"\u003eMicrosoft\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAspNetCore\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eComponents\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eForms\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eh3\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eEmployee\u003c/span\u003e \u003cspan class=\"n\"\u003eForm\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eh3\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eEditForm\u003c/span\u003e \u003cspan class=\"n\"\u003eModel\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;@employee\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003eOnValidSubmit\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;@HandleValidSubmit\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eDataAnnotationsValidator\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eValidationSummary\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ediv\u003c/span\u003e \u003cspan class=\"n\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;form-group\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eInputText\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Name\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003e@bind\u003c/span\u003e\u003cspan class=\"p\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;employee.Name\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;form-control\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eValidationMessage\u003c/span\u003e \u003cspan class=\"n\"\u003eFor\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;@(() =\u0026gt; employee.Name)\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ediv\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ebutton\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;submit\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;btn btn-primary\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eSubmit\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ebutton\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eEditForm\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e@code\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"n\"\u003eEmployee\u003c/span\u003e \u003cspan class=\"n\"\u003eemployee\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eEmployee\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"validation-with-fluentvalidation\"\u003eValidation with FluentValidation\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Blazored.FluentValidation\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eEmployeeValidator\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eAbstractValidator\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eEmployee\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eEmployeeValidator\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eRuleFor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eNotEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eWithMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Name is required\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e@page\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;/employee\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e@using\u003c/span\u003e \u003cspan class=\"n\"\u003eFluentValidation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e@using\u003c/span\u003e \u003cspan class=\"n\"\u003eFluentValidation\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eResults\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eh3\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eEmployee\u003c/span\u003e \u003cspan class=\"n\"\u003eForm\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eh3\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eEditForm\u003c/span\u003e \u003cspan class=\"n\"\u003eModel\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;@employee\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003eOnValidSubmit\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;@HandleValidSubmit\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eFluentValidationValidator\u003c/span\u003e \u003cspan class=\"n\"\u003eDisableAssemblyScanning\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;@true\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eValidationSummary\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ediv\u003c/span\u003e \u003cspan class=\"n\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;form-group\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eInputText\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Name\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003e@bind\u003c/span\u003e\u003cspan class=\"p\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eValue\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;employee.Name\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;form-control\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eValidationMessage\u003c/span\u003e \u003cspan class=\"n\"\u003eFor\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;@(() =\u0026gt; employee.Name)\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ediv\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ebutton\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;submit\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;btn btn-primary\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eSubmit\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ebutton\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eEditForm\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"custom-validation\"\u003eCustom Validation\u003c/h2\u003e","title":"Blazor Form Validation"},{"content":"Blazor applications can support multiple languages and cultures by using the globalization and localization features of .NET Core. This article describes how to implement globalization and localization in a Blazor application.\nBlazor WebAssembly \u0026lt;PropertyGroup\u0026gt; \u0026lt;BlazorWebAssemblyLoadAllGlobalizationData\u0026gt;true\u0026lt;/BlazorWebAssemblyLoadAllGlobalizationData\u0026gt; \u0026lt;/PropertyGroup\u0026gt; dotnet add package Microsoft.Extensions.Localization Blazor Server builder.Services.AddCustomLocalization(); app.UseCustomLocalization(); ","permalink":"/en/helloshop/webapp/blazor-globalization-localization/","summary":"\u003cp\u003eBlazor applications can support multiple languages and cultures by using the globalization and localization features of .NET Core. This article describes how to implement globalization and localization in a Blazor application.\u003c/p\u003e\n\u003ch2 id=\"blazor-webassembly\"\u003eBlazor WebAssembly\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;BlazorWebAssemblyLoadAllGlobalizationData\u0026gt;\u003c/span\u003etrue\u003cspan class=\"nt\"\u003e\u0026lt;/BlazorWebAssemblyLoadAllGlobalizationData\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/PropertyGroup\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Microsoft.Extensions.Localization\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"blazor-server\"\u003eBlazor Server\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddCustomLocalization\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUseCustomLocalization\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Blazor Globalization and Localization"},{"content":"About JavaScript Location Load a script in the \u0026lt;head\u0026gt; tag \u0026lt;head\u0026gt; \u0026lt;script src=\u0026#34;js/first.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; window.jsMethod = (methodParameter) =\u0026gt; { ... }; \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; Load a script in the \u0026lt;body\u0026gt; tag \u0026lt;body\u0026gt; \u0026lt;script src=\u0026#34;js/first.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; window.jsMethod = (methodParameter) =\u0026gt; { ... }; \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; Colocate with the component Component: CountComponent.razor Component: CountComponent.razor.cs Style: CountComponent.razor.css Script: CountComponent.razor.js Call JS from .NET Create an app.js file under wwwroot function helloWorld() { alert(\u0026#39;Hello World\u0026#39;); } function add(a, b) { return a + b; } Include the JS file in App.razor Script inclusion in the current web project\n\u0026lt;head\u0026gt; \u0026lt;script src=\u0026#34;@Assets[\u0026#34;app.js\u0026#34;]\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; If an RCL component library is used\n\u0026lt;head\u0026gt; \u0026lt;script src=\u0026#34;_content/LibraryName/app.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; Call JS methods from a .NET component @inject IJSRuntime JS \u0026lt;button class=\u0026#34;btn btn-primary\u0026#34; @onclick=\u0026#34;SayHello\u0026#34;\u0026gt;SayHello\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026#34;btn btn-primary\u0026#34; @onclick=\u0026#34;Add\u0026#34;\u0026gt;Add\u0026lt;/button\u0026gt; \u0026lt;p\u0026gt;Current count: @result\u0026lt;/p\u0026gt; @code { private int result = 0; private async Task SayHello() { await JS.InvokeVoidAsync(\u0026#34;helloWorld\u0026#34;); } private async Task Add() { result = await JS.InvokeAsync\u0026lt;int\u0026gt;(\u0026#34;add\u0026#34;, result, 1); StateHasChanged(); } } Module-based JS files export function helloWorld() { alert(\u0026#39;Hello World\u0026#39;); } export function add(a, b) { return a + b; } Write the following code in the MyComponent.razor component\n@inject IJSRuntime JS @implements IAsyncDisposable @code { private IJSInProcessObjectReference? module; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { var jsInProcess = (IJSInProcessRuntime)JS; module = await JS.InvokeAsync\u0026lt;IJSInProcessObjectReference\u0026gt;(\u0026#34;import\u0026#34;, \u0026#34;./path/to/MyComponent.razor.js\u0026#34;); var value = module.Invoke\u0026lt;string\u0026gt;(\u0026#34;javascriptFunctionIdentifier\u0026#34;); } } async ValueTask IAsyncDisposable.DisposeAsync() { try { if (module is not null) { await module.DisposeAsync(); } } catch (JSDisconnectedException) { } } } \u0026lt;div @ref=\u0026#34;divElement\u0026#34; style=\u0026#34;margin-top:2000px\u0026#34;\u0026gt; Set value via JS interop call: \u0026lt;strong\u0026gt;@scrollPosition\u0026lt;/strong\u0026gt; \u0026lt;/div\u0026gt; @code { private ElementReference divElement; private int scrollPosition; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { var jsInProcess = (IJSInProcessRuntime)JS; var module = await jsInProcess.InvokeAsync\u0026lt;IJSInProcessObjectReference\u0026gt;(\u0026#34;import\u0026#34;, \u0026#34;./path/to/MyComponent.razor.js\u0026#34;); scrollPosition = await module.InvokeAsync\u0026lt;int\u0026gt;(\u0026#34;getScrollPosition\u0026#34;, divElement); } } } Two static methods are provided\nDotNet.invokeMethodAsync(\u0026#39;{ASSEMBLY NAME}\u0026#39;, \u0026#39;{.NET METHOD ID}\u0026#39;, {ARGUMENTS}); DotNet.invokeMethod(\u0026#39;{ASSEMBLY NAME}\u0026#39;, \u0026#39;{.NET METHOD ID}\u0026#39;, {ARGUMENTS}); Provide a static method in the C# component\n@code { [JSInvokable] public static Task{\u0026lt;T\u0026gt;} {.NET METHOD ID}() { ... } } You can specify a parameter to change the method name\n[JSInvokable(\u0026#34;DifferentMethodName\u0026#34;)] public static Task{\u0026lt;T\u0026gt;} {.NET METHOD ID}() { ... } Call .NET methods via dotNetHelper A .NET instance is passed to JS by reference by wrapping it in a DotNetObjectReference and calling Create on it.\n@inject IJSRuntime JS @implements IAsyncDisposable @code { private DotNetObjectReference\u0026lt;MyComponent\u0026gt;? dotNetHelper; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { dotNetHelper = DotNetObjectReference.Create(this); await JS.InvokeVoidAsync(\u0026#34;jsFunction\u0026#34;, dotNetHelper); } } public async ValueTask DisposeAsync() { if (dotNetHelper is not null) { await dotNetHelper.DisposeAsync(); } } } ","permalink":"/en/helloshop/webapp/blazor-javascript/","summary":"\u003ch2 id=\"about-javascript-location\"\u003eAbout JavaScript Location\u003c/h2\u003e\n\u003ch3 id=\"load-a-script-in-the-head-tag\"\u003eLoad a script in the \u003ccode\u003e\u0026lt;head\u0026gt;\u003c/code\u003e tag\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e \u003cspan class=\"na\"\u003esrc\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;js/first.js\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nb\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ejsMethod\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003emethodParameter\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"load-a-script-in-the-body-tag\"\u003eLoad a script in the \u003ccode\u003e\u0026lt;body\u0026gt;\u003c/code\u003e tag\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e \u003cspan class=\"na\"\u003esrc\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;js/first.js\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nb\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ejsMethod\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003emethodParameter\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"colocate-with-the-component\"\u003eColocate with the component\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eComponent: CountComponent.razor\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eComponent: CountComponent.razor.cs\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eStyle: CountComponent.razor.css\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eScript: CountComponent.razor.js\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"call-js-from-net\"\u003eCall JS from .NET\u003c/h3\u003e\n\u003ch4 id=\"create-an-appjs-file-under-wwwroot\"\u003eCreate an app.js file under wwwroot\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nx\"\u003ehelloWorld\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;Hello World\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nx\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"include-the-js-file-in-apprazor\"\u003eInclude the JS file in App.razor\u003c/h4\u003e\n\u003cp\u003eScript inclusion in the current web project\u003c/p\u003e","title":"Blazor JavaScript Interoperability"},{"content":"What is GitHub Copilot? GitHub Copilot is an AI-powered coding assistant developed by GitHub and OpenAI. It can generate code snippets based on context, helping developers write code faster.\nHow to use GitHub Copilot GitHub Copilot can be used as an extension in Visual Studio Code and Visual Studio. After installation, suggestions are available while you write code.\nIs GitHub Copilot paid? GitHub Copilot is a paid product. During its early preview period, it was available for free for a limited time.\n","permalink":"/en/copilot/github-copilot/","summary":"\u003ch2 id=\"what-is-github-copilot\"\u003eWhat is GitHub Copilot?\u003c/h2\u003e\n\u003cp\u003eGitHub Copilot is an AI-powered coding assistant developed by GitHub and OpenAI. It can generate code snippets based on context, helping developers write code faster.\u003c/p\u003e\n\u003ch2 id=\"how-to-use-github-copilot\"\u003eHow to use GitHub Copilot\u003c/h2\u003e\n\u003cp\u003eGitHub Copilot can be used as an extension in Visual Studio Code and Visual Studio. After installation, suggestions are available while you write code.\u003c/p\u003e\n\u003ch2 id=\"is-github-copilot-paid\"\u003eIs GitHub Copilot paid?\u003c/h2\u003e\n\u003cp\u003eGitHub Copilot is a paid product. During its early preview period, it was available for free for a limited time.\u003c/p\u003e","title":"Coding with GitHub Copilot"},{"content":"Dapr Technical Architecture https://docs.dapr.io/concepts/overview\nInstalling the Dapr CLI Scaffolding Tool https://docs.dapr.io/zh-hans/getting-started/install-dapr-cli\nInitializing the Dapr Runtime with Containers https://docs.dapr.io/zh-hans/getting-started/install-dapr-selfhost\ndapr init Initializing the Dapr Runtime Offline Without Containers dapr init -s Initializing the Dapr Runtime from an Offline Bundle https://docs.dapr.io/zh-hans/operations/hosting/self-hosted/self-hosted-airgap/\ndapr init --from-dir C:\\daprbundle Opening the Default Initialization Directory explorer \u0026#34;$env:USERPROFILE\\.dapr\u0026#34; explorer \u0026#34;%USERPROFILE%\\.dapr\u0026#34; Viewing Dapr Runtime Status with the Dashboard dapr dashboard Configuring Distributed Events with Pub/Sub /components/redis-pubsub.yaml Redis-Based Pub/Sub Configuration apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: pubsub spec: type: pubsub.redis version: v1 metadata: - name: redisHost value: localhost:6379 - name: redisPassword value: \u0026#34;\u0026#34; RabbitMQ-Based Pub/Sub Configuration /components/rabbitmq-pubsub.yaml apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: pubsub spec: type: pubsub.rabbitmq version: v1 metadata: - name: host value: \u0026#34;amqp://guest:guestpwd@localhost:5672\u0026#34; - name: durable value: \u0026#34;false\u0026#34; - name: deletedWhenUnused value: \u0026#34;false\u0026#34; - name: autoAck value: \u0026#34;false\u0026#34; - name: reconnectWait value: \u0026#34;0\u0026#34; - name: concurrency value: parallel Starting the Sidecar and Application with the Dapr Command dapr run --app-id myapp --dapr-http-port 3500 --dapr-grpc-port 50001 --app-port 5000 --log-level debug --config ./configuration/config.yaml --components-path ./components dotnet run Starting the Sidecar and Application with Aspire dotnet add package Aspire.Hosting.Dapr var pubsub = builder.AddDaprPubSub(\u0026#34;pubsub\u0026#34;, new DaprComponentOptions { LocalPath = \u0026#34;./DaprComponents/\u0026#34; }); var productService = builder.AddProject\u0026lt;Projects.HelloWorld_ProductService\u0026gt;(\u0026#34;productservice\u0026#34;) .WithReference(identityService) .WithDaprSidecar() .WithReference(pubsub); RabbitMQ Pub/Sub Configuration var username = builder.AddParameter(\u0026#34;username\u0026#34;, secret: true); var password = builder.AddParameter(\u0026#34;password\u0026#34;, secret: true); var messaging = builder.AddRabbitMQ(\u0026#34;messaging\u0026#34;, username, password); // Service consumption builder.AddProject\u0026lt;Projects.ExampleProject\u0026gt;() .WithReference(messaging); ","permalink":"/en/helloshop/distributed-event-dapr/","summary":"\u003ch2 id=\"dapr-technical-architecture\"\u003eDapr Technical Architecture\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://docs.dapr.io/concepts/overview\"\u003ehttps://docs.dapr.io/concepts/overview\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"installing-the-dapr-cli-scaffolding-tool\"\u003eInstalling the Dapr CLI Scaffolding Tool\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://docs.dapr.io/zh-hans/getting-started/install-dapr-cli\"\u003ehttps://docs.dapr.io/zh-hans/getting-started/install-dapr-cli\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"initializing-the-dapr-runtime-with-containers\"\u003eInitializing the Dapr Runtime with Containers\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://docs.dapr.io/zh-hans/getting-started/install-dapr-selfhost\"\u003ehttps://docs.dapr.io/zh-hans/getting-started/install-dapr-selfhost\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edapr init\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"initializing-the-dapr-runtime-offline-without-containers\"\u003eInitializing the Dapr Runtime Offline Without Containers\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edapr init -s\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"initializing-the-dapr-runtime-from-an-offline-bundle\"\u003eInitializing the Dapr Runtime from an Offline Bundle\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://docs.dapr.io/zh-hans/operations/hosting/self-hosted/self-hosted-airgap/\"\u003ehttps://docs.dapr.io/zh-hans/operations/hosting/self-hosted/self-hosted-airgap/\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edapr init --from-dir C:\u003cspan class=\"se\"\u003e\\d\u003c/span\u003eaprbundle\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"opening-the-default-initialization-directory\"\u003eOpening the Default Initialization Directory\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eexplorer \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$env\u003c/span\u003e\u003cspan class=\"s2\"\u003e:USERPROFILE\\.dapr\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eexplorer \u003cspan class=\"s2\"\u003e\u0026#34;%USERPROFILE%\\.dapr\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"viewing-dapr-runtime-status-with-the-dashboard\"\u003eViewing Dapr Runtime Status with the Dashboard\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edapr dashboard\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"configuring-distributed-events-with-pubsub\"\u003eConfiguring Distributed Events with Pub/Sub\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e/components/redis-pubsub.yaml\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"redis-based-pubsub-configuration\"\u003eRedis-Based Pub/Sub Configuration\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eapiVersion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edapr.io/v1alpha1\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eComponent\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003emetadata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epubsub\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epubsub.redis\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eversion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ev1\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003emetadata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eredisHost\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003elocalhost:6379\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eredisPassword\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"rabbitmq-based-pubsub-configuration\"\u003eRabbitMQ-Based Pub/Sub Configuration\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e/components/rabbitmq-pubsub.yaml\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eapiVersion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edapr.io/v1alpha1\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eComponent\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003emetadata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epubsub\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epubsub.rabbitmq\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eversion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ev1\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003emetadata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ehost\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;amqp://guest:guestpwd@localhost:5672\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edurable\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;false\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edeletedWhenUnused\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;false\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eautoAck\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;false\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ereconnectWait\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003econcurrency\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eparallel\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"starting-the-sidecar-and-application-with-the-dapr-command\"\u003eStarting the Sidecar and Application with the Dapr Command\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edapr run --app-id myapp --dapr-http-port \u003cspan class=\"m\"\u003e3500\u003c/span\u003e --dapr-grpc-port \u003cspan class=\"m\"\u003e50001\u003c/span\u003e --app-port \u003cspan class=\"m\"\u003e5000\u003c/span\u003e --log-level debug --config ./configuration/config.yaml --components-path ./components dotnet run\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"starting-the-sidecar-and-application-with-aspire\"\u003eStarting the Sidecar and Application with Aspire\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Aspire.Hosting.Dapr\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epubsub\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddDaprPubSub\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;pubsub\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eDaprComponentOptions\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eLocalPath\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;./DaprComponents/\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eproductService\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddProject\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProjects\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHelloWorld_ProductService\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;productservice\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWithReference\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eidentityService\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWithDaprSidecar\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWithReference\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epubsub\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"rabbitmq-pubsub-configuration\"\u003eRabbitMQ Pub/Sub Configuration\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003evar \u003cspan class=\"nv\"\u003eusername\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e builder.AddParameter\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;username\u0026#34;\u003c/span\u003e, secret: \u003cspan class=\"nb\"\u003etrue\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003evar \u003cspan class=\"nv\"\u003epassword\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e builder.AddParameter\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;password\u0026#34;\u003c/span\u003e, secret: \u003cspan class=\"nb\"\u003etrue\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003evar \u003cspan class=\"nv\"\u003emessaging\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e builder.AddRabbitMQ\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;messaging\u0026#34;\u003c/span\u003e, username, password\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e// Service consumption\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebuilder.AddProject\u0026lt;Projects.ExampleProject\u0026gt;\u003cspan class=\"o\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       .WithReference\u003cspan class=\"o\"\u003e(\u003c/span\u003emessaging\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Configuring Distributed Events with Dapr Pub/Sub"},{"content":"Copyright Generally the copyright notice for a project in the .NET Foundation is given as \u0026ldquo;Copyright (c) .NET Foundation and Contributors. All Rights Reserved\u0026rdquo;. The copyright notice should be placed in the LICENSE for the project so the beginning of the LICENSE for an MIT Licensed project would be:\nThe MIT License (MIT) Copyright (c) .NET Foundation and Contributors All Rights Reserved Permission is hereby granted... And an Apache 2.0 Licensed project would begin\nCopyright (c) .NET Foundation and Contributors All Rights Reserved Apache License, Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions... When including third party open source code in a project, any third party copyright notices should be preserved with the code. It is good practise to also surface any third party notices required in your project into a file called NOTICE.md (or ThirdPartyNotices.txt) in the project root.\nFile Headers The following file header format is the used for .NET Foundation projects. Please use it for new files and update the headers of existing code files as appropriate. Note that the general copyright notice for the project is given in the project\u0026rsquo;s LICENSE file but any third party copyright notices should be preserved.\n// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the \u0026lt;\u0026lt;LICENSE_TYPE\u0026gt;\u0026gt; license. // See the LICENSE file in the project root for more information. Therefore the file header for an MIT licensed project would be:\n// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. Where-as the file header for an Apache 2.0 licensed project would be:\n// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. The addition of existing files from other projects is handled on a case by casebasis.\n","permalink":"/en/visualstudio/copyright/","summary":"\u003ch2 id=\"copyright\"\u003eCopyright\u003c/h2\u003e\n\u003cp\u003eGenerally the copyright notice for a project in the .NET Foundation is given as\n\u0026ldquo;Copyright (c) .NET Foundation and Contributors. All Rights Reserved\u0026rdquo;.\nThe copyright notice should be placed in the LICENSE for the project so\nthe beginning of the LICENSE for an MIT Licensed project would be:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eThe MIT License (MIT)\n\nCopyright (c) .NET Foundation and Contributors\nAll Rights Reserved\n\nPermission is hereby granted... \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd an Apache 2.0 Licensed project would begin\u003c/p\u003e","title":"Copyright"},{"content":"Warehouse naming convention Create a code repository named HelloShop on Github. Regarding the naming convention of the code repository, use the SnakeCaseLower nomenclature.\nNaming policy Original Converted PascalCase HelloShop HelloShop CamelCase HelloShop helloShop SnakeCaseLower HelloShop hello_shop SnakeCaseUpper HelloShop HELLO_SHOP KebabCaseLower HelloShop hello-shop KebabCaseUpper HelloShop HELLO-SHOP Warehouse folder structure Create a folder named src in the repository to store the source code.\nDocumentation Create a file named README.md in the warehouse to store the warehouse documentation. Of course, each folder should have a documentation if possible.\nignore file The function of the gitignore file is to specify files that do not need to be submitted to the code repository, such as compiled files, log files, etc.\nhttps://github.com/github/gitignore/blob/main/VisualStudio.gitignore\nproperties file The function of the gitattributes file is to specify the attributes of the file, such as the newline character of the file, the encoding of the file, etc.\nDirectory structure assets Static resources, including pictures, icons, videos, audios, etc.\nbuild Build scripts, including compilation scripts, packaging scripts, release scripts, etc.\ndocs Related documents, including design documents, architecture documents, development documents, deployment documents, etc. `\nsamples Demonstration examples, including code examples, configuration examples, data examples, document examples, etc.\nsrc Source code, including source code, configuration files, resource files, script files, etc.\ntests Test code, including unit testing, integration testing, end-to-end testing, etc.\ntools Tools used by the project.\nRepository Branches Repository branches are used to manage versions of the repository, such as master, develop, release, hotfix, feature, etc.\nRepository Labels Warehouse tags are used to mark the version of the warehouse, such as v1.0.0, v1.0.1, v1.1.0, v2.0.0, etc.\n","permalink":"/en/helloshop/git-repositories/","summary":"\u003ch2 id=\"warehouse-naming-convention\"\u003eWarehouse naming convention\u003c/h2\u003e\n\u003cp\u003eCreate a code repository named HelloShop on Github. Regarding the naming convention of the code repository, use the SnakeCaseLower nomenclature.\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eNaming policy\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eOriginal\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eConverted\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003ePascalCase\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eCamelCase\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehelloShop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eSnakeCaseLower\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehello_shop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eSnakeCaseUpper\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHELLO_SHOP\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eKebabCaseLower\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehello-shop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eKebabCaseUpper\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHELLO-SHOP\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"warehouse-folder-structure\"\u003eWarehouse folder structure\u003c/h2\u003e\n\u003cp\u003eCreate a folder named src in the repository to store the source code.\u003c/p\u003e\n\u003ch2 id=\"documentation\"\u003eDocumentation\u003c/h2\u003e\n\u003cp\u003eCreate a file named README.md in the warehouse to store the warehouse documentation. Of course, each folder should have a documentation if possible.\u003c/p\u003e","title":"Creating Code Repositories"},{"content":"\nMicrosoft Visio Visio is Microsoft\u0026rsquo;s diagramming tool. It is very powerful, but it is paid, and it only runs on Windows, with no support for Mac or Linux. Although Microsoft has released Visio Online, its features are still fairly limited, and it is also paid.\nThe Open-Source Drawio Tool Drawio is an open-source diagramming tool. It supports both online and desktop use, multiple formats, multiple platforms, multiple languages, many kinds of shapes, many export formats, many plugins, and many integration options. It also supports diagramming in Visual Studio Code via a plugin.\nVector Graphics and Bitmaps Vector graphics are shapes described by mathematical formulas; they can be scaled up infinitely without losing fidelity. Bitmaps are images described by pixels and become distorted when enlarged.\nWhere to Start Drawing Drawio official site\nDraw online\nContainers and Container Resize Scaling Setting a shape\u0026rsquo;s Container property to true combines multiple shapes into a single shape, which you can then scale. Setting the Collapsed property to true collapses the shape so that only its title is shown. Setting the Resizable property to false prevents child shapes of the container from being resized.\nFloating Links and Fixed Links A floating link is one whose position moves as the shape moves; a fixed link is one whose position stays put when the shape moves. You can also add custom connection points.\nDragging Connection Line Labels Labels on connection lines can be made draggable, so you can drag a connection line\u0026rsquo;s label to any position along the line, and you can customize the position of connection points.\nConnection Line Styles and Reversal You can set the style of a connection line, reverse a connection line, and adjust how much a connection line bends.\nText Position of Shapes and Connection Lines By setting the text position, you can place text at the top-left corner of a shape or connection line; by setting the text position to center, you can place the text in the middle of the shape.\nAdding Waypoints to Connection Lines You can add waypoints to connection lines, add arrows to connection lines, and add labels to connection lines.\nAny Target Can Be Given a Link You can set a hyperlink on any object: on a connection line, or on a shape.\nReplacing Shapes You can drag a new shape onto an existing shape to replace it; after replacement, the shape\u0026rsquo;s properties and its connection lines are preserved.\nAlignment and Distribution You can set how shapes are aligned, and how they are distributed.\nDrawing with Styles You can draw with styles active, apply a style to shapes, or clear the default style.\nEmbedded Images and Online Images Understand the differences between geometric shapes, shape library shapes, embedded images, and online images.\nModifying Styles of Embedded SVG Images Use the Alibaba icon library iconfont to find icons, download the SVG image, and embed it into Drawio. You can modify the style of the SVG image, but you need to configure a rule, as in the code below.\n\u0026lt;svg\u0026gt; \u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; .st1{fill:#ffffff;stroke:#ffffff;} \u0026lt;/style\u0026gt; \u0026lt;path class=\u0026#34;st1\u0026#34; d=\u0026#34;points\u0026#34;/\u0026gt; \u0026lt;/svg\u0026gt; editableCssRules=.*; SVG Export Format Options Set the zoom factor, border, background color, image copies, embedded images, and link open behavior.\nConnection Line Flow Animation Set the Flow Animation property to true to enable a flow animation on the connection line. This feature is built on CSS animation capabilities — simple to use and sufficient for most needs.\nCustom Complex SVG Animations With SVG you can create custom animations, setting the start time, duration, repeat count, animation type, animation attributes, animation values, animation path, animation direction, animation speed, animation delay, animation fill, and the state after the animation ends.\nW3C animation standard for SVG\nDefine a simple path animation.\n\u0026lt;circle r=\u0026#34;3.5\u0026#34; fill=\u0026#34;#00aa9e\u0026#34;\u0026gt; \u0026lt;animateMotion dur=\u0026#34;5s\u0026#34; repeatCount=\u0026#34;indefinite\u0026#34; path=\u0026#34;points\u0026#34;/\u0026gt; \u0026lt;/circle\u0026gt; Elegantly connect animations using XLINK\n\u0026lt;path id=\u0026#34;stage1-1\u0026#34; d=\u0026#34;points\u0026#34;/\u0026gt; \u0026lt;circle r=\u0026#34;3\u0026#34; fill=\u0026#34;red\u0026#34;\u0026gt; \u0026lt;animateMotion dur=\u0026#34;5s\u0026#34; repeatCount=\u0026#34;indefinite\u0026#34;\u0026gt; \u0026lt;mpath xlink:href=\u0026#34;#stage1-1\u0026#34;/\u0026gt; \u0026lt;/animateMotion\u0026gt; \u0026lt;/circle\u0026gt; ","permalink":"/en/helloshop/drawio/","summary":"\u003cp\u003e\u003cimg alt=\"2024-04-09-07-17-04\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/2024-04-09-07-17-04.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"microsoft-visio\"\u003eMicrosoft Visio\u003c/h2\u003e\n\u003cp\u003eVisio is Microsoft\u0026rsquo;s diagramming tool. It is very powerful, but it is paid, and it only runs on Windows, with no support for Mac or Linux. Although Microsoft has released Visio Online, its features are still fairly limited, and it is also paid.\u003c/p\u003e\n\u003ch2 id=\"the-open-source-drawio-tool\"\u003eThe Open-Source Drawio Tool\u003c/h2\u003e\n\u003cp\u003eDrawio is an open-source diagramming tool. It supports both online and desktop use, multiple formats, multiple platforms, multiple languages, many kinds of shapes, many export formats, many plugins, and many integration options. It also supports diagramming in Visual Studio Code via a plugin.\u003c/p\u003e","title":"Creating Dynamic Flowcharts and Architecture Diagrams"},{"content":"Different database objects follow different naming conventions. To accommodate different databases, EF Core provides several naming convention configuration options.\nCommon Naming Styles Naming policy Original Converted PascalCase HelloShop HelloShop CamelCase HelloShop helloShop SnakeCaseLower HelloShop hello_shop SnakeCaseUpper HelloShop HELLO_SHOP KebabCaseLower HelloShop hello-shop KebabCaseUpper HelloShop HELLO-SHOP You can use the Humanizr library to convert strings between different naming styles.\nSingular or Plural Table Names By default, EF Core uses the singular form for table names. You can use a convention-based configurator or manually specify plural table names. The community debate over singular versus plural table names has always existed and there is no unified standard, but Zero recommends the singular form.\nManually Specifying Table and Column Names Table Names By default, EF Core uses the entity type name as the table name. You can change the table name by overriding the OnModelCreating method:\nprotected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity\u0026lt;Blog\u0026gt;().ToTable(\u0026#34;blogs\u0026#34;); } Column Names By default, EF Core uses property names as column names. You can change a column name with the HasColumnName method:\nprotected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity\u0026lt;Blog\u0026gt;().Property(b =\u0026gt; b.Url).HasColumnName(\u0026#34;blog_url\u0026#34;); } PostgreSQL Naming Conventions PostgreSQL naming conventions use lowercase snake_case — words separated by underscores, such as blog_url — with singular table names.\nUsing the EFCore.NamingConventions Library The EFCore.NamingConventions library provides naming convention configuration options that make it easy to configure table names, column names, and so on.\nInstalling the EFCore.NamingConventions Library dotnet add package EFCore.NamingConventions Configuring the Naming Convention Configure the naming convention using the OnConfiguring method:\nprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); optionsBuilder.UseSnakeCaseNamingConvention(); } Or configure it in the dependency injection container:\nbuilder.Services.AddDbContext\u0026lt;MyDbContext\u0026gt;(options =\u0026gt; options.UseNpgsql().UseSnakeCaseNamingConvention()); Changing the Migrations History Table Name By default, EF Core keeps track of which migrations have been applied to the database by recording them in a table named __EFMigrationsHistory. For various reasons, you may want to customize this table to better suit your needs.\noptions.UseSqlServer(connectionString,x =\u0026gt; x.MigrationsHistoryTable(\u0026#34;__MyMigrationsHistory\u0026#34;, \u0026#34;mySchema\u0026#34;)); https://learn.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/history-table\n","permalink":"/en/helloshop/efcore-naming-conventions/","summary":"\u003cp\u003eDifferent database objects follow different naming conventions. To accommodate different databases, EF Core provides several naming convention configuration options.\u003c/p\u003e\n\u003ch2 id=\"common-naming-styles\"\u003eCommon Naming Styles\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eNaming policy\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eOriginal\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eConverted\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003ePascalCase\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eCamelCase\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehelloShop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eSnakeCaseLower\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehello_shop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eSnakeCaseUpper\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHELLO_SHOP\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eKebabCaseLower\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehello-shop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eKebabCaseUpper\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHELLO-SHOP\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eYou can use the \u003ca href=\"https://github.com/Humanizr/Humanizer\"\u003eHumanizr\u003c/a\u003e library to convert strings between different naming styles.\u003c/p\u003e\n\u003ch2 id=\"singular-or-plural-table-names\"\u003eSingular or Plural Table Names\u003c/h2\u003e\n\u003cp\u003eBy default, EF Core uses the singular form for table names. You can use a convention-based configurator or manually specify plural table names. The community debate over singular versus plural table names has always existed and there is no unified standard, but Zero recommends the singular form.\u003c/p\u003e","title":"Database Object Naming Conventions"},{"content":"Permission Definitions Each microservice is autonomous, and permission definitions are also the responsibility of each microservice. They each define their own permissions. The permission manager will automatically merge permission definitions. The permission definition context is a collection of permission definitions within the microservice and global permission definitions. The permission provider will provide permission data based on the permission definition context. Permission grouping is to facilitate the management of permission definitions. Permission grouping is part of the permission definition. The permission provider is automatically injected into the container to provide permission query endpoints.\nArchitecture design Permission extension method IServiceCollection AddPermissionDefinitions(this IServiceCollection services) EndpointRouteBuilder MapPermissionDefinitions(this IEndpointRouteBuilder endpoints, params string[] tags) ","permalink":"/en/helloshop/permission-definition/","summary":"\u003ch2 id=\"permission-definitions\"\u003ePermission Definitions\u003c/h2\u003e\n\u003cp\u003eEach microservice is autonomous, and permission definitions are also the responsibility of each microservice. They each define their own permissions. The permission manager will automatically merge permission definitions. The permission definition context is a collection of permission definitions within the microservice and global permission definitions. The permission provider will provide permission data based on the permission definition context. Permission grouping is to facilitate the management of permission definitions. Permission grouping is part of the permission definition. The permission provider is automatically injected into the container to provide permission query endpoints.\u003c/p\u003e","title":"Designing Permission Definitions and Permission Providers"},{"content":"Component-Based Development Component-based development allows developers to encapsulate UI components as independent Blazor components and reuse them across applications. This approach improves the maintainability and reusability of the code, similar to modern front-end frameworks such as React and Vue.\nComponent = HTML + CSS + JS\nBlazor component = Razor + CSS + C#\nIntroduction to MAUI MAUI is the cross-platform UI framework introduced in .NET 6, which allows developers to create native applications using C# and XAML. It is the evolution of Xamarin.Forms and supports Android, iOS, macOS, and Windows.\nIntroduction to Blazor Blazor is a framework for building interactive web applications that allows developers to share code between the client and the server using C# and Razor syntax. Blazor supports both WebAssembly and server-side rendering modes, so developers can choose the appropriate mode to build their applications as needed.\nMAUI + Blazor Blazor Hybrid applications combine the strengths of MAUI and Blazor, allowing developers to embed Blazor components inside MAUI applications. In this way, developers can use Blazor\u0026rsquo;s component-based development style while enjoying the native UI and cross-platform support that MAUI provides.\nComponent Reuse Architecture Diagram Blazor Rendering Modes Rendering in web development technologies falls into two modes: CSR (client-side rendering) and SSR (server-side rendering). Blazor also has two rendering modes: Blazor WebAssembly and Blazor Server.\nBlazor WebAssembly Blazor WebAssembly is a Blazor application mode that runs in the browser. It packages all of the application\u0026rsquo;s code and dependencies into a WebAssembly file and executes it on the client. The advantage of this mode is fast user interaction and offline support, but it can be limited by browser performance.\nBlazor Server Blazor Server is a Blazor application mode that runs on the server. It communicates with the client over SignalR, handling both UI updates and event processing on the server side. The advantage of this mode is that it can leverage the server\u0026rsquo;s computing power, but it requires a persistent network connection, which can lead to latency and performance issues.\nBlazor Auto Blazor Auto is an application mode that automatically selects the Blazor rendering mode: it automatically chooses either Blazor WebAssembly or Blazor Server based on the application\u0026rsquo;s runtime environment and performance requirements. The advantage of this mode is that it can achieve the best performance and user experience in different environments.\nStatic Prerendering Blazor also supports static prerendering, which can prerender Blazor components on the server and serve them to the client as static HTML files. This mode can improve the application\u0026rsquo;s loading speed and SEO performance.\n","permalink":"/en/helloshop/webapp/blazor-app/","summary":"\u003ch2 id=\"component-based-development\"\u003eComponent-Based Development\u003c/h2\u003e\n\u003cp\u003eComponent-based development allows developers to encapsulate UI components as independent Blazor components and reuse them across applications. This approach improves the maintainability and reusability of the code, similar to modern front-end frameworks such as React and Vue.\u003c/p\u003e\n\u003cp\u003eComponent = HTML + CSS + JS\u003c/p\u003e\n\u003cp\u003eBlazor component = Razor + CSS + C#\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"blazor-component\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/webapp/blazor-component.svg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"introduction-to-maui\"\u003eIntroduction to MAUI\u003c/h2\u003e\n\u003cp\u003eMAUI is the cross-platform UI framework introduced in .NET 6, which allows developers to create native applications using C# and XAML. It is the evolution of Xamarin.Forms and supports Android, iOS, macOS, and Windows.\u003c/p\u003e","title":"Developing Blazor Hybrid Apps"},{"content":"Building and Designing an API Gateway An API gateway is a server that acts as an intermediary between clients and backend services. It receives requests from clients and forwards them to the backend services. An API gateway can also perform other tasks, such as authentication, monitoring, load balancing, caching, request analysis, and logging.\nThe BFF Pattern for Aggregating Multiple Services An API gateway can aggregate multiple services based on the BFF (Backend for Frontend) pattern. When a client requests data, the API gateway can call multiple services to fetch the data and then aggregate it into a single response returned to the client, reducing the number of round trips between the client and backend services and thereby improving performance.\nCommon API Gateways Nginx, Envoy, Ocelot, Yarp, Zuul, Kong, Traefik, Tyk, Ambassador, Gloo, AWS API Gateway, Azure API Management, Google Cloud Endpoints, Apigee, MuleSoft Anypoint API Gateway, WSO2 API Manager, TIBCO Mashery, 3scale API Management, Akana, CA API Gateway, Dell Boomi, DreamFactory, IBM API Connect, Mashape, Mashery, MuleSoft Anypoint Platform, OpenLegacy, Oracle API Platform Cloud Service, Repre.\nOpen-Source API Gateways Based on .NET Ocelot and Yarp are open-source API gateways based on .NET that can be used to build and design API gateways in a microservice architecture. Yarp provides many features, such as routing, load balancing, authentication, authorization, rate limiting, circuit breaking, retries, caching, request forwarding, request rewriting, request logging, response caching, response compression, response rewriting, response logging, monitoring, tracing, cross-origin support, security, performance, extensibility, configurability, manageability, observability, testability, deployability, maintainability, and upgradability.\nGithub open-source repository\nOfficial reference documentation\nBuild and Design an API Gateway with Yarp dotnet add package Yarp.ReverseProxy Configure Yarp with a Configuration File { \u0026#34;ReverseProxy\u0026#34;: { \u0026#34;Routes\u0026#34;: { \u0026#34;testserviceRoute\u0026#34;: { \u0026#34;ClusterId\u0026#34;: \u0026#34;testServiceCluster\u0026#34;, \u0026#34;Match\u0026#34;: { \u0026#34;Path\u0026#34;: \u0026#34;testservice/{**remainder}\u0026#34; }, \u0026#34;Transforms\u0026#34;: [ { \u0026#34;PathPattern\u0026#34;: \u0026#34;{**remainder}\u0026#34; } ] } }, \u0026#34;Clusters\u0026#34;: { \u0026#34;testServiceCluster\u0026#34;: { \u0026#34;Destinations\u0026#34;: { \u0026#34;testServiceCluster/destination1\u0026#34;: { \u0026#34;Address\u0026#34;: \u0026#34;http://identityservice\u0026#34; } } } } } } Service Discovery in .NET Service discovery is a mechanism for discovering and locating services; it helps clients find a service\u0026rsquo;s location and address. Service discovery can be used when building and designing API gateways in a microservice architecture, so that clients can access backend services through the API gateway.\nUsing Service Discovery https://learn.microsoft.com/zh-cn/dotnet/core/extensions/service-discovery\ndotnet add package Microsoft.Extensions.ServiceDiscovery --prerelease builder.Services.AddServiceDiscovery(); builder.Services.ConfigureHttpClientDefaults(static http =\u0026gt; { // Turn on service discovery by default http.UseServiceDiscovery(); }); { \u0026#34;Services\u0026#34;: { \u0026#34;catalog\u0026#34;: { \u0026#34;https\u0026#34;: [ \u0026#34;localhost:8080\u0026#34;, \u0026#34;10.46.24.90:80\u0026#34; ] } } } Using Service Discovery in a Reverse Proxy dotnet add package Microsoft.Extensions.ServiceDiscovery.Yarp ","permalink":"/en/helloshop/yarp-reverse-proxy/","summary":"\u003ch2 id=\"building-and-designing-an-api-gateway\"\u003eBuilding and Designing an API Gateway\u003c/h2\u003e\n\u003cp\u003eAn API gateway is a server that acts as an intermediary between clients and backend services. It receives requests from clients and forwards them to the backend services. An API gateway can also perform other tasks, such as authentication, monitoring, load balancing, caching, request analysis, and logging.\u003c/p\u003e\n\u003ch2 id=\"the-bff-pattern-for-aggregating-multiple-services\"\u003eThe BFF Pattern for Aggregating Multiple Services\u003c/h2\u003e\n\u003cp\u003eAn API gateway can aggregate multiple services based on the BFF (Backend for Frontend) pattern. When a client requests data, the API gateway can call multiple services to fetch the data and then aggregate it into a single response returned to the client, reducing the number of round trips between the client and backend services and thereby improving performance.\u003c/p\u003e","title":"Gateway and Service Discovery"},{"content":"Swagger is a language-agnostic specification for describing REST APIs. It lets computers and users understand what a REST API does without directly accessing the source code. The Swagger project was donated to the OpenAPI Initiative, and since then it has been known as the OpenAPI Specification.\nSwashbuckle is an open-source project for .NET Core. It is a collection of OpenAPI tools used to generate OpenAPI specification documents. It can generate API documentation automatically, including API descriptions, request and response formats, parameter descriptions, and more.\nInstalling Swashbuckle.AspNetCore In a .NET Core project, you can install the Swashbuckle.AspNetCore package via NuGet.\ndotnet add package Swashbuckle.AspNetCore Configuring Swashbuckle In the Startup.cs file, you can configure Swagger as follows:\nbuilder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } ","permalink":"/en/helloshop/openapi/","summary":"\u003cp\u003eSwagger is a language-agnostic specification for describing REST APIs. It lets computers and users understand what a REST API does without directly accessing the source code. The Swagger project was donated to the OpenAPI Initiative, and since then it has been known as the OpenAPI Specification.\u003c/p\u003e\n\u003cp\u003eSwashbuckle is an open-source project for .NET Core. It is a collection of OpenAPI tools used to generate OpenAPI specification documents. It can generate API documentation automatically, including API descriptions, request and response formats, parameter descriptions, and more.\u003c/p\u003e","title":"Generating API Documentation with the OpenAPI Specification"},{"content":"Getting the Time the Traditional Way We usually use DateTime.Now and DateTimeOffset.Now to get the current time, but code written this way is hard to test: the time depends on the system clock, which is difficult to control and reproduce — the only way to change it is to change the system time.\nvar now1 = DateTime.Now; var nowUtc1 = DateTime.UtcNow; var now2 = DateTimeOffset.Now; var nowUtc2 = DateTimeOffset.UtcNow; The New TimeProvider Type To solve the problems above, .NET 8 introduced the TimeProvider type, which can be used to get the time and makes it easy to control the time in tests.\nvar now = TimeProvider.System.GetLocalNow(); var nowUtc = TimeProvider.System.GetUtcNow(); Overriding TimeProvider to Simulate the Time We can override the TimeProvider type to simulate the time, which makes testing easier.\npublic class MyTimeProvider : TimeProvider { public override DateTimeOffset GetUtcNow() =\u0026gt; DateTimeOffset.UtcNow; } Dependency Injection of a TimeProvider Implementation The default implementation, TimeProvider.System, uses the system time.\nbuilder.Services.AddSingleton(TimeProvider.System); Inject MyTimeProvider to use a custom time source.\nbuilder.Services.AddSingleton\u0026lt;TimeProvider, MyTimeProvider\u0026gt;(); Code That Uses TimeProvider public class MyService(TimeProvider timeProvider) { public void DoSomething() { var now = timeProvider.GetLocalNow(); var nowUtc = timeProvider.GetUtcNow(); } } A demonstration of using TimeProvider in the Zero framework.\nUnit Testing Time-Dependent Code Microsoft.Extensions.TimeProvider.Testing\n","permalink":"/en/helloshop/use-time-provider/","summary":"\u003ch2 id=\"getting-the-time-the-traditional-way\"\u003eGetting the Time the Traditional Way\u003c/h2\u003e\n\u003cp\u003eWe usually use \u003ccode\u003eDateTime.Now\u003c/code\u003e and \u003ccode\u003eDateTimeOffset.Now\u003c/code\u003e to get the current time, but code written this way is hard to test: the time depends on the system clock, which is difficult to control and reproduce — the only way to change it is to change the system time.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003enow1\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003enowUtc1\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003enow2\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003enowUtc2\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"the-new-timeprovider-type\"\u003eThe New TimeProvider Type\u003c/h2\u003e\n\u003cp\u003eTo solve the problems above, .NET 8 introduced the TimeProvider type, which can be used to get the time and makes it easy to control the time in tests.\u003c/p\u003e","title":"Getting the Time with TimeProvider"},{"content":"Globalization means a web application can adapt to different cultures and regions without code changes. Localization means a web application can display different content based on the user\u0026rsquo;s culture and region. Globalization and localization are two different concepts, but they are often used together.\nThere are many ways to implement multiple languages: resource files, databases, configuration files, and so on. This article focuses on the resource file approach, which is also what Microsoft officially recommends. Other options such as PO files and JSON files can also achieve multilingual support, but they are less convenient than resource files.\nCreating resource files Welcome.en-US.resx Welcome.zh-CN.resx Adding localization services and resource location public static IServiceCollection AddCustomLocalization(this IServiceCollection services) { services.AddLocalization(options =\u0026gt; options.ResourcesPath = \u0026#34;Resources\u0026#34;); return services; } Using the localization service public class HelloWorldController(IStringLocalizerFactory stringLocalizerFactory) : ControllerBase { [HttpGet] public IActionResult Get() { var location = Assembly.GetExecutingAssembly().FullName; ArgumentException.ThrowIfNullOrWhiteSpace(location); var localizer = stringLocalizerFactory.Create(\u0026#34;Welcome\u0026#34;, location); return Ok(localizer[\u0026#34;HelloWorld\u0026#34;].Value); } } Setting the language from HTTP requests The UseRequestLocalization middleware reads the language setting from the request and then sets the current thread\u0026rsquo;s language so it can be used by subsequent requests. This achieves app-wide localization without having to configure localization in every controller.\npublic static IApplicationBuilder UseCustomLocalization(this IApplicationBuilder app) { var supportedCultures = new[] { \u0026#34;zh-CN\u0026#34;, \u0026#34;en-US\u0026#34; }; var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures.First()) .AddSupportedCultures(supportedCultures) .AddSupportedUICultures(supportedCultures); app.UseRequestLocalization(localizationOptions); return app; } Use the following approaches to supply the language setting from a Header, QueryString, or Cookie:\nAccept-Language: en-US http://localhost:5000/?culture=en-Us .AspNetCore.Culture=en-US Setting Accept-Language in OpenApi for multilingual support services.Configure\u0026lt;SwaggerGenOptions\u0026gt;(options =\u0026gt; options.OperationFilter\u0026lt;AcceptLanguageHeaderOperationFilter\u0026gt;()); public class AcceptLanguageHeaderOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var parameter = new OpenApiParameter { Name = HeaderNames.AcceptLanguage, In = ParameterLocation.Header, Required = false, Schema = new OpenApiSchema { Default = new OpenApiString(\u0026#34;zh-CN\u0026#34;), Type = \u0026#34;string\u0026#34;, Enum = [new OpenApiString(\u0026#34;zh-CN\u0026#34;), new OpenApiString(\u0026#34;en-US\u0026#34;)] } }; operation.Parameters.Add(parameter); } } Globalizing data display public class HelloWorldController(IStringLocalizerFactory stringLocalizerFactory) : ControllerBase { [HttpGet(\u0026#34;time\u0026#34;)] public IActionResult GetTime( { //string amount=88.88.ToString(\u0026#34;C\u0026#34;); return Ok(TimeProvider.System.GetUtcNow().ToString()); } } Localizing models and properties IStringLocalizerFactory localizerFactory = app.ApplicationServices.GetRequiredService\u0026lt;IStringLocalizerFactory\u0026gt;(); ValidatorOptions.Global.DisplayNameResolver = (type, memberInfo, lambdaExpression) =\u0026gt; { string displayName = memberInfo.Name; DisplayAttribute? displayAttribute = memberInfo.GetCustomAttribute\u0026lt;DisplayAttribute\u0026gt;(true); displayName = displayAttribute?.Name ?? displayName; DisplayNameAttribute? displayNameAttribute = memberInfo.GetCustomAttribute\u0026lt;DisplayNameAttribute\u0026gt;(true); displayName = displayNameAttribute?.DisplayName ?? displayName; var localizer = localizerFactory.Create(type); return localizer[displayName]; }; Localizing validation error messages public UserCreateRequestValidator(IdentityServiceDbContext dbContext, IStringLocalizer\u0026lt;UserCreateRequest\u0026gt; localizer) { RuleFor(m =\u0026gt; m.PhoneNumber).NotNull().NotEmpty().Length(11).Matches(@\u0026#34;^1\\d{10}$\u0026#34;).Must((model, phoneNumber) =\u0026gt; { return !dbContext.Users.Any(e =\u0026gt; e.PhoneNumber == phoneNumber); }).WithMessage(localizer[\u0026#34;PhoneNumberExists\u0026#34;]); } Microsoft Resx editing tools Searching Bing for the keyword Resx Editor turns up many tools that can edit Resx files.\nhttps://learn.microsoft.com/zh-cn/dotnet/framework/tools\nTranslating resource files with Bing Searching Bing for the keyword Resx Translation turns up many translation tools that can translate English resource files into other languages.\nhttps://github.com/salarcode/AutoResxTranslator\nhttps://github.com/stevencohn/ResxTranslator\nhttps://github.com/HakanL/resxtranslator\nhttps://apps.microsoft.com/detail/9mtpd7jzxnnn\n","permalink":"/en/helloshop/localization/","summary":"\u003cp\u003eGlobalization means a web application can adapt to different cultures and regions without code changes. Localization means a web application can display different content based on the user\u0026rsquo;s culture and region. Globalization and localization are two different concepts, but they are often used together.\u003c/p\u003e\n\u003cp\u003eThere are many ways to implement multiple languages: resource files, databases, configuration files, and so on. This article focuses on the resource file approach, which is also what Microsoft officially recommends. Other options such as PO files and JSON files can also achieve multilingual support, but they are less convenient than resource files.\u003c/p\u003e","title":"Globalization and Localization"},{"content":"Introduction to Git Git is an open source distributed version control system for agile and efficient handling of any project, small or large.\nClone repository Clone the remote repository locally, make modifications locally, and commit to the remote repository.\nCreate repository Create a repository locally and push the local repository to the remote repository. Regarding the use of naming conventions for code repositories, use the KebabCaseLower nomenclature.\nNaming policy Original Converted PascalCase HelloShop HelloShop CamelCase HelloShop helloShop SnakeCaseLower HelloShop hello_shop SnakeCaseUpper HelloShop HELLO_SHOP KebabCaseLower HelloShop hello-shop KebabCaseUpper HelloShop HELLO-SHOP Local warehouse settings Git settings include global settings and repository settings, and multiple remote repositories can be set up.\nDocumentation Create a file named README.md in the repository to store the documentation for the repository. Of course, each folder should have a documentation if possible.\nignore file The role of the gitignore file is to specify files that do not need to be submitted to the code repository, such as compiled files, log files, etc.\nhttps://github.com/github/gitignore/blob/main/VisualStudio.gitignore\nRoutine operation Fetch, pull, commit, push, sync, rollback, label, conflict, staging (row), pull.\nbranch management Repository branches are used to manage versions of the repository, such as master, develop, release, hotfix, feature, etc. The main operations include: creating branches, switching branches, merging branches, and deleting branches.\nRepository management Browse repositories, manage repositories, work with multiple repositories, resolve merge conflicts.\n","permalink":"/en/visualstudio/git-with-visual-studio/","summary":"\u003ch2 id=\"introduction-to-git\"\u003eIntroduction to Git\u003c/h2\u003e\n\u003cp\u003eGit is an open source distributed version control system for agile and efficient handling of any project, small or large.\u003c/p\u003e\n\u003ch2 id=\"clone-repository\"\u003eClone repository\u003c/h2\u003e\n\u003cp\u003eClone the remote repository locally, make modifications locally, and commit to the remote repository.\u003c/p\u003e\n\u003ch2 id=\"create-repository\"\u003eCreate repository\u003c/h2\u003e\n\u003cp\u003eCreate a repository locally and push the local repository to the remote repository. Regarding the use of naming conventions for code repositories, use the KebabCaseLower nomenclature.\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eNaming policy\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eOriginal\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eConverted\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003ePascalCase\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eCamelCase\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehelloShop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eSnakeCaseLower\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehello_shop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eSnakeCaseUpper\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHELLO_SHOP\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eKebabCaseLower\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ehello-shop\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eKebabCaseUpper\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHelloShop\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eHELLO-SHOP\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"local-warehouse-settings\"\u003eLocal warehouse settings\u003c/h2\u003e\n\u003cp\u003eGit settings include global settings and repository settings, and multiple remote repositories can be set up.\u003c/p\u003e","title":"How to use Git version control gracefully"},{"content":"ASP.NET Core Identity architecture ASP.NET Core Identity is a mature identity system that provides user management, role management, claim management, password management, sign-in management, external login management, two-factor authentication, email confirmation, phone number confirmation, security token management, user lockout, user unlock, user sign-out, user deletion, user recovery, and more.\nIdentity architecture explained Entity type Description User Represents a user. Role Represents a role. UserClaim Represents a claim that a user possesses. UserToken Represents an authentication token for a user. UserLogin Associates a user with a login. RoleClaim Represents a claim granted to all users within a role. UserRole A join entity that links users and roles. Storing Identity data with EF Core Defining entity types public class User : IdentityUser\u0026lt;int\u0026gt; { public DateTimeOffset CreationTime { get; set; } = DateTimeOffset.UtcNow; } public class Role : IdentityRole\u0026lt;int\u0026gt; { public DateTimeOffset CreationTime { get; set; } = DateTimeOffset.UtcNow; } Configuring entity types public class UserEntityTypeConfiguration : IEntityTypeConfiguration\u0026lt;User\u0026gt; { public void Configure(EntityTypeBuilder\u0026lt;User\u0026gt; builder) { builder.ToTable(\u0026#34;Users\u0026#34;); builder.HasKey(x =\u0026gt; x.Id); builder.Property(x =\u0026gt; x.CreationTime); } } Reusing the Identity database context dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore public class IdentityServiceDbContext(DbContextOptions\u0026lt;IdentityServiceDbContext\u0026gt; options) : IdentityDbContext\u0026lt;User, Role, int\u0026gt;(options) { protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); AppContext.SetSwitch(\u0026#34;Npgsql.EnableLegacyTimestampBehavior\u0026#34;, true); AppContext.SetSwitch(\u0026#34;Npgsql.DisableDateTimeInfinityConversions\u0026#34;, true); } } Using the Identity API endpoints builder.Services.AddIdentityApiEndpoints\u0026lt;User\u0026gt;(options =\u0026gt; { options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequiredLength = 5; options.SignIn.RequireConfirmedAccount = false; }).AddEntityFrameworkStores\u0026lt;IdentityServiceDbContext\u0026gt;(); app.MapGroup(\u0026#34;identity\u0026#34;).MapIdentityApi\u0026lt;User\u0026gt;(); Working with Identity data Services such as UserManager, RoleManager, SignInManager, and PasswordValidator can all be used to work with Identity data. Unless there is a special case, it is not recommended to manipulate Identity data directly with a DbContext.\nUsing the Endpoints Explorer In Visual Studio 2022 or later, open this feature via \u0026ldquo;View\u0026rdquo; \u0026gt; \u0026ldquo;Other Windows\u0026rdquo; \u0026gt; \u0026ldquo;Endpoints Explorer\u0026rdquo;.\nTesting API endpoints Tools such as the CURL command-line tool, the Edge network console, OpenApi, HTTP files, Postman, Insomnia, and the Visual Studio Code REST Client extension can all be used to test API endpoints.\nBearer authentication Authorization : Bearer \u0026lt;token string\u0026gt; Integrating Bearer authentication with OpenApi services.Configure\u0026lt;SwaggerGenOptions\u0026gt;(options =\u0026gt; { options.AddSecurityDefinition(\u0026#34;bearerAuth\u0026#34;, new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = \u0026#34;bearer\u0026#34;, BearerFormat = \u0026#34;JWT\u0026#34;, Description = \u0026#34;JWT Authorization header using the Bearer scheme.\u0026#34; }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = \u0026#34;bearerAuth\u0026#34; } }, Array.Empty\u0026lt;string\u0026gt;() } }); }); References https://learn.microsoft.com/zh-cn/aspnet/core/security/authentication/identity-api-authorization\nhttps://www.cnblogs.com/dongfo/p/17808249.html\n","permalink":"/en/helloshop/identity-api-endpoints/","summary":"\u003ch2 id=\"aspnet-core-identity-architecture\"\u003eASP.NET Core Identity architecture\u003c/h2\u003e\n\u003cp\u003eASP.NET Core Identity is a mature identity system that provides user management, role management, claim management, password management, sign-in management, external login management, two-factor authentication, email confirmation, phone number confirmation, security token management, user lockout, user unlock, user sign-out, user deletion, user recovery, and more.\u003c/p\u003e\n\u003ch2 id=\"identity-architecture-explained\"\u003eIdentity architecture explained\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eEntity type\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eDescription\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eUser\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eRepresents a user.\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eRole\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eRepresents a role.\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eUserClaim\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eRepresents a claim that a user possesses.\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eUserToken\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eRepresents an authentication token for a user.\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eUserLogin\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eAssociates a user with a login.\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eRoleClaim\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eRepresents a claim granted to all users within a role.\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eUserRole\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eA join entity that links users and roles.\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"storing-identity-data-with-ef-core\"\u003eStoring Identity data with EF Core\u003c/h2\u003e\n\u003ch3 id=\"defining-entity-types\"\u003eDefining entity types\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUser\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIdentityUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e \u003cspan class=\"n\"\u003eCreationTime\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eRole\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIdentityRole\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e \u003cspan class=\"n\"\u003eCreationTime\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"configuring-entity-types\"\u003eConfiguring entity types\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserEntityTypeConfiguration\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIEntityTypeConfiguration\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eConfigure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEntityTypeBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToTable\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Users\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasKey\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreationTime\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"reusing-the-identity-database-context\"\u003eReusing the Identity database context\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edotnet\u003c/span\u003e \u003cspan class=\"k\"\u003eadd\u003c/span\u003e \u003cspan class=\"n\"\u003epackage\u003c/span\u003e \u003cspan class=\"n\"\u003eMicrosoft\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAspNetCore\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentity\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEntityFrameworkCore\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDbContextOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIdentityDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eRole\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprotected\u003c/span\u003e \u003cspan class=\"kd\"\u003eoverride\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eOnModelCreating\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eModelBuilder\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ebase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOnModelCreating\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eApplyConfigurationsFromAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetExecutingAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eAppContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetSwitch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Npgsql.EnableLegacyTimestampBehavior\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eAppContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetSwitch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Npgsql.DisableDateTimeInfinityConversions\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"using-the-identity-api-endpoints\"\u003eUsing the Identity API endpoints\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddIdentityApiEndpoints\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireDigit\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireLowercase\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireUppercase\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireNonAlphanumeric\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequiredLength\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSignIn\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRequireConfirmedAccount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}).\u003c/span\u003e\u003cspan class=\"n\"\u003eAddEntityFrameworkStores\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMapGroup\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;identity\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eMapIdentityApi\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"working-with-identity-data\"\u003eWorking with Identity data\u003c/h2\u003e\n\u003cp\u003eServices such as UserManager, RoleManager, SignInManager, and PasswordValidator can all be used to work with Identity data. Unless there is a special case, it is not recommended to manipulate Identity data directly with a DbContext.\u003c/p\u003e","title":"Identity System and Identity API Endpoints"},{"content":"Abstracting the Distributed Bus Using design patterns, the way a distributed bus is used is abstracted, defining the basic capabilities of the bus.\nEvent Bus-Based Components The distributed event bus is implemented in a componentized way, encapsulating the implementation details of the bus inside components.\nRabbitMQ-based implementation: https://github.com/dotnet/eShop/tree/main/src/EventBusRabbitMQ\nThe Dapr Implementation of the Event Bus Dapr is an open-source distributed application runtime. Its publish and subscribe module enables microservices to communicate with each other using event-driven architecture messaging. Dapr provides an implementation of the event bus: publish events via Dapr, expose Dapr subscription endpoints, and route messages to different event handlers.\n","permalink":"/en/helloshop/distributed-events/","summary":"\u003ch2 id=\"abstracting-the-distributed-bus\"\u003eAbstracting the Distributed Bus\u003c/h2\u003e\n\u003cp\u003eUsing design patterns, the way a distributed bus is used is abstracted, defining the basic capabilities of the bus.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"event-bus-abstraction\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/event-bus-abstraction.svg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"event-bus-based-components\"\u003eEvent Bus-Based Components\u003c/h2\u003e\n\u003cp\u003eThe distributed event bus is implemented in a componentized way, encapsulating the implementation details of the bus inside components.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"event-bus-component\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/event-bus-component.svg\"\u003e\u003c/p\u003e\n\u003cp\u003eRabbitMQ-based implementation: \u003ca href=\"https://github.com/dotnet/eShop/tree/main/src/EventBusRabbitMQ\"\u003ehttps://github.com/dotnet/eShop/tree/main/src/EventBusRabbitMQ\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"the-dapr-implementation-of-the-event-bus\"\u003eThe Dapr Implementation of the Event Bus\u003c/h2\u003e\n\u003cp\u003eDapr is an open-source distributed application runtime. Its publish and subscribe module enables microservices to communicate with each other using event-driven architecture messaging. Dapr provides an implementation of the event bus: publish events via Dapr, expose Dapr subscription endpoints, and route messages to different event handlers.\u003c/p\u003e","title":"Implementing a Distributed Event Bus with Dapr"},{"content":"What Is the gRPC Communication Framework gRPC is a high-performance, open source, general-purpose RPC framework developed by Google. It is based on HTTP/2 and supports multiple languages (such as Go, Java, Python, C++, Node.js, Ruby, C#, Objective-C, PHP, and Dart).\ngRPC uses Protocol Buffers as its interface definition language (IDL). Protocol Buffers is a lightweight and efficient structured data serialization method, similar to XML or JSON, but smaller, faster, and simpler.\nImplementing a Service with gRPC in ASP.NET Core To define a gRPC service, you need to create a gRPC service definition file (a .proto file) and then use the gRPC tools to generate the server and client code.\ngRPC Services with OpenAPI dotnet add package Microsoft.AspNetCore.Grpc.Swagger \u0026lt;IncludeHttpRuleProtos\u0026gt;true\u0026lt;/IncludeHttpRuleProtos\u0026gt; var builder = WebApplication.CreateBuilder(args); builder.Services.AddGrpc().AddJsonTranscoding(); builder.Services.AddGrpcSwagger(); builder.Services.AddSwaggerGen(c =\u0026gt; { c.SwaggerDoc(\u0026#34;v1\u0026#34;, new OpenApiInfo { Title = \u0026#34;gRPC transcoding\u0026#34;, Version = \u0026#34;v1\u0026#34; }); }); var app = builder.Build(); app.UseSwagger(); if (app.Environment.IsDevelopment()) app.UseSwaggerUI(c =\u0026gt; { c.SwaggerEndpoint(\u0026#34;/swagger/v1/swagger.json\u0026#34;, \u0026#34;My API V1\u0026#34;); }); } app.MapGrpcService\u0026lt;GreeterService\u0026gt;(); app.Run(); // My amazing greeter service. service Greeter { // Sends a greeting. rpc SayHello (HelloRequest) returns (HelloReply) { option (google.api.http) = { get: \u0026#34;/v1/greeter/{name}\u0026#34; }; } } message HelloRequest { // Name to say hello to. string name = 1; } message HelloReply { // Hello reply message. string message = 1; } Unit Testing gRPC Services dotnet add package moq public class GreeterServiceTests { [Fact] public async Task SayHello_ReturnsHello() { // Arrange var mockLogger = new Mock\u0026lt;ILogger\u0026lt;GreeterService\u0026gt;\u0026gt;(); var service = new GreeterService(mockLogger.Object); var request = new HelloRequest { Name = \u0026#34;Unit Test\u0026#34; }; var context = new ServerCallContext(); // Act var reply = await service.SayHello(request, context); // Assert Assert.Equal(\u0026#34;Hello Unit Test\u0026#34;, reply.Message); } } Integration Testing gRPC Services dotnet add package Microsoft.AspNetCore.TestHost public class GreeterServiceIntegrationTests { private readonly TestServer _server; private readonly Greeter.GreeterClient _client; public GreeterServiceIntegrationTests() { _server = new TestServer(new WebHostBuilder() .ConfigureServices(services =\u0026gt; { services.AddGrpc(); }) .Configure(app =\u0026gt; { app.UseRouting(); app.UseEndpoints(endpoints =\u0026gt; { endpoints.MapGrpcService\u0026lt;GreeterService\u0026gt;(); }); })); _client = new Greeter.GreeterClient(_server.CreateGrpcChannel()); } [Fact] public async Task SayHello_ReturnsHello() { // Arrange var request = new HelloRequest { Name = \u0026#34;Integration Test\u0026#34; }; // Act var reply = await _client.SayHelloAsync(request); // Assert Assert.Equal(\u0026#34;Hello Integration Test\u0026#34;, reply.Message); } } ","permalink":"/en/helloshop/basket-service-grpc/","summary":"\u003ch2 id=\"what-is-the-grpc-communication-framework\"\u003eWhat Is the gRPC Communication Framework\u003c/h2\u003e\n\u003cp\u003egRPC is a high-performance, open source, general-purpose RPC framework developed by Google. It is based on HTTP/2 and supports multiple languages (such as Go, Java, Python, C++, Node.js, Ruby, C#, Objective-C, PHP, and Dart).\u003c/p\u003e\n\u003cp\u003egRPC uses Protocol Buffers as its interface definition language (IDL). Protocol Buffers is a lightweight and efficient structured data serialization method, similar to XML or JSON, but smaller, faster, and simpler.\u003c/p\u003e","title":"Implementing the Basket Service with gRPC"},{"content":"CQRS is short for Command Query Responsibility Segregation. It is a pattern that separates read operations from update operations: queries return results without changing system state and have no side effects, while commands change system state. Idempotency means that executing the same command multiple times produces the same result with no side effects.\nWhy Use the CQRS Pattern In traditional architectures, the same data model is used to query and update the database. This is simple and works well for basic CRUD operations.\nCQRS separates reads and writes into distinct models: commands update data, and queries read data. The read store can be a read-only replica of the write store, or the read and write stores can have completely different structures. Using multiple read-only replicas can improve query performance, especially in distributed scenarios where the read replicas are located close to the application instances.\nImplementing the Command Pattern in CQRS with MediatR dotnet add package MediatR public class CreateOrderCommand : IRequest\u0026lt;bool\u0026gt; { public string Product { get; set; } public int Quantity { get; set; } } public class CreateOrderCommandHandler1 : IRequestHandler\u0026lt;CreateOrderCommand, bool\u0026gt; { public Task\u0026lt;bool\u0026gt; Handle(CreateOrderCommand request, CancellationToken cancellationToken) { return Task.FromResult(1); } } public class CreateOrderCommandHandler2 : IRequestHandler\u0026lt;CreateOrderCommand, bool\u0026gt; { public Task\u0026lt;bool\u0026gt; Handle(CreateOrderCommand request, CancellationToken cancellationToken) { return Task.FromResult(2); } } public class OrdersController : ControllerBase { private readonly IMediator _mediator; public OrdersController(IMediator mediator) { _mediator = mediator; } [HttpPost] public async Task\u0026lt;IActionResult\u0026gt; CreateOrder(CreateOrderCommand command) { var orderId = await _mediator.Send(command); return Ok(orderId); } } builder.Services.AddMediatR(options =\u0026gt; options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); Handling CQRS Commands with the Mediator Request Pipeline LoggingBehavior, ValidatorBehavior, and TransactionBehavior\nbuilder.Services.AddMediatR(options =\u0026gt; { options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); options.AddOpenBehavior(typeof(LoggingBehavior\u0026lt;,\u0026gt;)); options.AddOpenBehavior(typeof(ValidatorBehavior\u0026lt;,\u0026gt;)); options.AddOpenBehavior(typeof(TransactionBehavior\u0026lt;,\u0026gt;)); }); Or use the Pipeline extension methods\nbuilder.Services.AddScoped(typeof(IPipelineBehavior\u0026lt;,\u0026gt;), typeof(LoggingBehavior\u0026lt;,\u0026gt;)); builder.Services.AddScoped(typeof(IPipelineBehavior\u0026lt;,\u0026gt;), typeof(ValidatorBehavior\u0026lt;,\u0026gt;)); builder.Services.AddScoped(typeof(IPipelineBehavior\u0026lt;,\u0026gt;), typeof(TransactionBehavior\u0026lt;,\u0026gt;)); Message Deduplication Mechanism Implementing the Query Pattern in CQRS public interface IOrderQueries { Task\u0026lt;OrderDetails\u0026gt; GetOrderAsync(int id); Task\u0026lt;IEnumerable\u0026lt;OrderSummary\u0026gt;\u0026gt; GetOrdersFromUserAsync(int userId); } builder.Services.AddScoped\u0026lt;IOrderQueries,OrderQueries\u0026gt;(); Handling Integration Events with Dapr.AspNetCore dotnet add package Dapr.AspNetCore services.AddDaprClient(); Refactoring Folders The EntityFrameworks folder is renamed to the Infrastructure folder\nUnder the BasketService microservice\u0026rsquo;s Infrastructure folder, add a Repositories folder\nImplementing the Outbox Pattern builder.Services.AddTransient\u0026lt;IDistributedEventLogService, DistributedEventLogService\u0026lt;OrderingServiceDbContext\u0026gt;\u0026gt;(); Generic Type Mapping and Validation References https://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs\nhttps://learn.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns\nhttps://www.xcode.me/Training?keyword=cqrs\n","permalink":"/en/helloshop/ordering-service/","summary":"\u003cp\u003eCQRS is short for Command Query Responsibility Segregation. It is a pattern that separates read operations from update operations: queries return results without changing system state and have no side effects, while commands change system state. Idempotency means that executing the same command multiple times produces the same result with no side effects.\u003c/p\u003e\n\u003ch2 id=\"why-use-the-cqrs-pattern\"\u003eWhy Use the CQRS Pattern\u003c/h2\u003e\n\u003cp\u003eIn traditional architectures, the same data model is used to query and update the database. This is simple and works well for basic CRUD operations.\u003c/p\u003e","title":"Implementing the Ordering Service with the CQRS Pattern"},{"content":"Three Elements of Authorization Subject, resource, and operation. For example: a user (subject) reading and writing (operations) a file (resource). The relationship among subject, resource, and operation is called an authorization relation. Based on this relationship, we can abstract it into a triple (subject, resource, operation), and this triple is the basic unit of authorization.\nRole-Based Access Control Role-Based Access Control (RBAC) is an access control model that manages users\u0026rsquo; access to resources through roles. A role is a collection of permissions, and users acquire the corresponding permissions by being assigned roles. The role-based access control model is simple and easy to use. There are other access control models as well, such as Attribute-Based Access Control (ABAC).\nPermission Access Control List An Access Control List (ACL) is a permission control model used to manage users\u0026rsquo; access to resources. An ACL controls users\u0026rsquo; operation permissions on a resource by defining an access control list for each resource.\nACL Storage Design public class PermissionGranted { public int Id { get; set; } public int RoleId { get; set; } public required string PermissionName { get; set; } public string? ResourceType { get; set; } public string? ResourceId { get; set; } } Configuring the ACL Entity in the DbContext public void Configure(EntityTypeBuilder\u0026lt;PermissionGranted\u0026gt; builder) { builder.ToTable(\u0026#34;PermissionGranted\u0026#34;); builder.Property(x =\u0026gt; x.Id); builder.Property(x =\u0026gt; x.PermissionName).HasMaxLength(64); builder.Property(x =\u0026gt; x.ResourceType).HasMaxLength(16); builder.Property(x =\u0026gt; x.ResourceId).HasMaxLength(32); builder.HasOne\u0026lt;Role\u0026gt;().WithMany().HasForeignKey(x =\u0026gt; x.RoleId).IsRequired(); builder.HasIndex(x =\u0026gt; new { x.RoleId, x.PermissionName, x.ResourceType, x.ResourceId }).IsUnique(); } Permission ACL Storage Design public class PermissionGranted { public int Id { get; set; } public int RoleId { get; set; } public required string PermissionName { get; set; } public string? ResourceType { get; set; } public string? ResourceId { get; set; } } Configuring the ACL Entity in the DbContext public void Configure(EntityTypeBuilder\u0026lt;PermissionGranted\u0026gt; builder) { builder.ToTable(\u0026#34;PermissionGranted\u0026#34;); builder.Property(x =\u0026gt; x.Id); builder.Property(x =\u0026gt; x.PermissionName).HasMaxLength(64); builder.Property(x =\u0026gt; x.ResourceType).HasMaxLength(16); builder.Property(x =\u0026gt; x.ResourceId).HasMaxLength(32); builder.HasOne\u0026lt;Role\u0026gt;().WithMany().HasForeignKey(x =\u0026gt; x.RoleId).IsRequired(); builder.HasIndex(x =\u0026gt; new { x.RoleId, x.PermissionName, x.ResourceType, x.ResourceId }).IsUnique(); } Designing a Permission Checker public interface IPermissionChecker { Task\u0026lt;bool\u0026gt; IsGrantedAsync(string name, string? resourceType = null, string? resourceId = null); Task\u0026lt;bool\u0026gt; IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name, string? resourceType = null, string? resourceId = null); } Implementing the Abstract Permission Checker Iterate over the permissions of each role; if any role has the permission, return true to indicate that authorization is granted.\npublic abstract class PermissionChecker : IPermissionChecker { public abstract Task\u0026lt;bool\u0026gt; IsGrantedAsync(string name, string? resourceType = null, string? resourceId = null); public abstract Task\u0026lt;bool\u0026gt; IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name, string? resourceType = null, string? resourceId = null); } Implementing Permission Checkers The ACL list is stored in the database. You can implement a local permission checker via the DbContext, or a remote permission checker via HttpClient.\npublic class RemotePermissionChecker: PermissionChecker public class LocalPermissionChecker: PermissionChecker ","permalink":"/en/helloshop/access-control-list/","summary":"\u003ch2 id=\"three-elements-of-authorization\"\u003eThree Elements of Authorization\u003c/h2\u003e\n\u003cp\u003eSubject, resource, and operation. For example: a user (subject) reading and writing (operations) a file (resource). The relationship among subject, resource, and operation is called an authorization relation. Based on this relationship, we can abstract it into a triple (subject, resource, operation), and this triple is the basic unit of authorization.\u003c/p\u003e\n\u003ch2 id=\"role-based-access-control\"\u003eRole-Based Access Control\u003c/h2\u003e\n\u003cp\u003eRole-Based Access Control (RBAC) is an access control model that manages users\u0026rsquo; access to resources through roles. A role is a collection of permissions, and users acquire the corresponding permissions by being assigned roles. The role-based access control model is simple and easy to use. There are other access control models as well, such as Attribute-Based Access Control (ABAC).\u003c/p\u003e","title":"Implementing the Permission Access Control List"},{"content":"Creating Entities HelloWorld.ProductService.Entities.Products Product \u0026amp; CatalogBrand Using PostgreSQL with EF Core dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL Creating the DbContext namespace HelloWorld.ProductService.EntityFrameworks { public class ProductServiceDbContext(DbContextOptions\u0026lt;ProductServiceDbContext\u0026gt; options) : DbContext(options) { protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); } } } Creating Entity Configuration Classes namespace HelloWorld.ProductService.EntityFrameworks.EntityConfigurations.Products { public class ProductEntityTypeConfiguration : IEntityTypeConfiguration\u0026lt;Product\u0026gt; { public void Configure(EntityTypeBuilder\u0026lt;Product\u0026gt; builder) { builder.ToTable(\u0026#34;Products\u0026#34;); builder.Property(x =\u0026gt; x.Name).HasMaxLength(32); builder.HasOne(x =\u0026gt; x.Brand).WithMany(); } } } Database Connection String { \u0026#34;ConnectionStrings\u0026#34;: { \u0026#34;ProductDatabase\u0026#34;: \u0026#34;Host=localhost;Port=5432;Database=ProductService;Username=postgres;Password=postgres\u0026#34; } } Registering the DbContext builder.Services.AddDbContext\u0026lt;ProductServiceDbContext\u0026gt;(options =\u0026gt; { options.UseNpgsql(builder.Configuration.GetConnectionString(DbConstants.ConnectionStringName)); }); Migrating the Database dotnet tool install --global dotnet-ef dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add InitialCreate --output-dir EntityFrameworks/Migrations dotnet ef database update Creating Models namespace HelloWorld.ProductService.Models.Products { public class ProductCreateRequest { public required string Name { get; init; } public string? Description { get; init; } public decimal Price { get; init; } public int BrandId { get; init; } public string? ImageUrl { get; init; } } } Creating Automatic Mappings namespace HelloWorld.ProductService.AutoMapper { public class ProductsMapConfiguration : Profile { public ProductsMapConfiguration() { CreateMap\u0026lt;ProductCreateRequest, Product\u0026gt;(); CreateMap\u0026lt;ProductUpdateRequest, Product\u0026gt;(); CreateMap\u0026lt;Product, ProductListItem\u0026gt;().AfterMap((src, dest) =\u0026gt; dest.BrandName = src.Brand.Name); CreateMap\u0026lt;Product, ProductDetailsResponse\u0026gt;(); CreateMap\u0026lt;BrandCreateRequest, Brand\u0026gt;(); CreateMap\u0026lt;BrandUpdateRequest, Brand\u0026gt;(); CreateMap\u0026lt;Brand, BrandDetailsResponse\u0026gt;(); CreateMap\u0026lt;Brand, BrandListItem\u0026gt;(); } } } Creating Validators namespace HelloWorld.ProductService.Validations.Products { public class BrandUpdateRequestValidator : AbstractValidator\u0026lt;BrandUpdateRequest\u0026gt; { public BrandUpdateRequestValidator() { RuleFor(x =\u0026gt; x.Id).GreaterThan(0); RuleFor(x =\u0026gt; x.Name).NotNull().NotEmpty().Length(8, 32); } } } Creating Controllers namespace HelloWorld.ProductService.Controllers { [Route(\u0026#34;api/[controller]\u0026#34;)] [ApiController] public class ProductsController : ControllerBase } Defining Permissions namespace HelloWorld.ProductService.PermissionProviders { public static class CatalogPermissions public class CatalogPermissionDefiitionProvider : IPermissionDefinitionProvider } Localizing Permissions CatalogPermissionDefinitionProvider.en-US.resx CatalogPermissionDefinitionProvider.zh-CN.resx Localizing Entity Properties namespace HelloWorld.ProductService.Entities.Products { public class Product { [Display(Name = \u0026#34;ProductName\u0026#34;)] public string Name { get; set; } } } Repetitive Work Repetitive work can be reduced with code generation tools, such as Visual Studio plugins, T4 templates, Roslyn, and so on.\n","permalink":"/en/helloshop/product-service/","summary":"\u003ch2 id=\"creating-entities\"\u003eCreating Entities\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eHelloWorld\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProductService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEntities\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProducts\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003eCatalogBrand\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"using-postgresql-with-ef-core\"\u003eUsing PostgreSQL with EF Core\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Npgsql.EntityFrameworkCore.PostgreSQL\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-the-dbcontext\"\u003eCreating the DbContext\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.EntityFrameworks\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eProductServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDbContextOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProductServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003eprotected\u003c/span\u003e \u003cspan class=\"kd\"\u003eoverride\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eOnModelCreating\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eModelBuilder\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ebase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOnModelCreating\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eApplyConfigurationsFromAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetExecutingAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-entity-configuration-classes\"\u003eCreating Entity Configuration Classes\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.EntityFrameworks.EntityConfigurations.Products\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eProductEntityTypeConfiguration\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIEntityTypeConfiguration\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eConfigure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEntityTypeBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToTable\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Products\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eHasMaxLength\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e32\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasOne\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eBrand\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eWithMany\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"database-connection-string\"\u003eDatabase Connection String\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;ConnectionStrings\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;ProductDatabase\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Host=localhost;Port=5432;Database=ProductService;Username=postgres;Password=postgres\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"registering-the-dbcontext\"\u003eRegistering the DbContext\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProductServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUseNpgsql\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConfiguration\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetConnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDbConstants\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConnectionStringName\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"migrating-the-database\"\u003eMigrating the Database\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet tool install --global dotnet-ef\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Microsoft.EntityFrameworkCore.Design\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet ef migrations add InitialCreate --output-dir EntityFrameworks/Migrations\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet ef database update\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-models\"\u003eCreating Models\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.Models.Products\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eProductCreateRequest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003erequired\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eDescription\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003edecimal\u003c/span\u003e \u003cspan class=\"n\"\u003ePrice\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eBrandId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eImageUrl\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-automatic-mappings\"\u003eCreating Automatic Mappings\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.AutoMapper\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eProductsMapConfiguration\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eProfile\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eProductsMapConfiguration\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProductCreateRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProductUpdateRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eProductListItem\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;().\u003c/span\u003e\u003cspan class=\"n\"\u003eAfterMap\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003esrc\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003edest\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eBrandName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esrc\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eBrand\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProduct\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eProductDetailsResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eBrandCreateRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eBrand\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eBrandUpdateRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eBrand\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eBrand\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eBrandDetailsResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eCreateMap\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eBrand\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eBrandListItem\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-validators\"\u003eCreating Validators\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.Validations.Products\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eBrandUpdateRequestValidator\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eAbstractValidator\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eBrandUpdateRequest\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eBrandUpdateRequestValidator\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eRuleFor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eGreaterThan\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eRuleFor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eNotNull\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eNotEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eLength\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e32\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-controllers\"\u003eCreating Controllers\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.Controllers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [Route(\u0026#34;api/[controller]\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;)]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [ApiController]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eProductsController\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eControllerBase\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"defining-permissions\"\u003eDefining Permissions\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.PermissionProviders\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCatalogPermissions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCatalogPermissionDefiitionProvider\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIPermissionDefinitionProvider\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"localizing-permissions\"\u003eLocalizing Permissions\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eCatalogPermissionDefinitionProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003een\u003c/span\u003e\u003cspan class=\"p\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eUS\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eresx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eCatalogPermissionDefinitionProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ezh\u003c/span\u003e\u003cspan class=\"p\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eCN\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eresx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"localizing-entity-properties\"\u003eLocalizing Entity Properties\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"nn\"\u003eHelloWorld.ProductService.Entities.Products\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eProduct\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e        [Display(Name = \u0026#34;ProductName\u0026#34;)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"repetitive-work\"\u003eRepetitive Work\u003c/h2\u003e\n\u003cp\u003eRepetitive work can be reduced with code generation tools, such as Visual Studio plugins, T4 templates, Roslyn, and so on.\u003c/p\u003e","title":"Implementing the Product Management Microservice"},{"content":"CQRS stands for Command Query Responsibility Segregation. In the CQRS pattern, commands and queries are implemented separately: commands handle write operations while queries handle read operations. This better fulfills the single responsibility principle and also enables better performance optimization.\nhttps://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs\nhttps://www.xcode.me/Training/UnitNote/576\nDefining the Order Query Interface public interface IOrderQueries { Task\u0026lt;OrderDetails\u0026gt; GetOrderAsync(int id); } Implementing the Order Query Interface public class OrderQueries : IOrderQueries { private readonly OrderingContext _context; public OrderQueries(OrderingContext context) { _context = context; dbContext.Database.SetConnectionString(\u0026#34;db2_connection_string\u0026#34;); } public async Task\u0026lt;OrderDetails\u0026gt; GetOrderAsync(int id) { // do something } } Registering the Order Query Interface services.AddScoped\u0026lt;IOrderQueries, OrderQueries\u0026gt;(); Using the Order Query Interface public class OrderController : ControllerBase { private readonly IOrderQueries _orderQueries; public OrderController(IOrderQueries orderQueries) { _orderQueries = orderQueries; } [HttpGet(\u0026#34;{id}\u0026#34;)] public async Task\u0026lt;IActionResult\u0026gt; GetOrderAsync(int id) { var order = await _orderQueries.GetOrderAsync(id); return Ok(order); } } ","permalink":"/en/helloshop/ordering-service-cqrs-query/","summary":"\u003cp\u003eCQRS stands for Command Query Responsibility Segregation. In the CQRS pattern, commands and queries are implemented separately: commands handle write operations while queries handle read operations. This better fulfills the single responsibility principle and also enables better performance optimization.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs\"\u003ehttps://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.xcode.me/Training/UnitNote/576\"\u003ehttps://www.xcode.me/Training/UnitNote/576\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"defining-the-order-query-interface\"\u003eDefining the Order Query Interface\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003einterface\u003c/span\u003e \u003cspan class=\"nc\"\u003eIOrderQueries\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderDetails\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetOrderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"implementing-the-order-query-interface\"\u003eImplementing the Order Query Interface\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eOrderQueries\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIOrderQueries\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderingContext\u003c/span\u003e \u003cspan class=\"n\"\u003e_context\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderingContext\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_context\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDatabase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetConnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;db2_connection_string\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eOrderDetails\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetOrderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"c1\"\u003e// do something\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"registering-the-order-query-interface\"\u003eRegistering the Order Query Interface\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eservices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddScoped\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIOrderQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"using-the-order-query-interface\"\u003eUsing the Order Query Interface\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eOrderController\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eControllerBase\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eIOrderQueries\u003c/span\u003e \u003cspan class=\"n\"\u003e_orderQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderController\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIOrderQueries\u003c/span\u003e \u003cspan class=\"n\"\u003eorderQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_orderQueries\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eorderQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003e    [HttpGet(\u0026#34;{id}\u0026#34;)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIActionResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eGetOrderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eorder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003e_orderQueries\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetOrderAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eOk\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eorder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Implementing the Query Pattern in CQRS"},{"content":"Caching is a common technique aimed at improving the performance and scalability of a system. To cache data, frequently accessed data is temporarily copied into fast storage located close to the application. If this fast data store is closer to the application than the original source, caching can dramatically improve the response time of client applications by serving data more quickly.\nCache Types Private In-Memory Cache A private cache means the caching service runs on the same server as the application; it is part of the application, usually a library or module. A private cache is typically an in-memory cache, such as MemoryCache or ConcurrentDictionary. It is a lightweight cache suitable for small applications. The advantage of a private cache is that it is simple and easy to use; the drawback is that data cannot be shared across servers, making it unsuitable for large applications. The IMemoryCache interface can be used to work with a private cache in .NET.\nservices.AddMemoryCache(); public class MyService(IMemoryCache cache) : IService { public async Task Test() { cache.Set(\u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;); var value = cache.Get(\u0026#34;key\u0026#34;); } } Distributed Shared Cache A distributed cache means the caching service runs across multiple servers; it is an independent service that can share data across servers. A distributed cache is typically an in-memory cache such as Redis, Memcached, or Garnet. It is a high-performance, low-latency cache suitable for large applications. The advantages of a distributed cache are high performance and low latency, with support for large-scale data storage and access; the drawbacks are higher complexity and the need for additional server resources. The IDistributedCache interface can be used to work with a distributed cache in .NET.\nservices.AddMemoryDistributedCache(); public class MyService(IDistributedCache cache) : IService { public async Task Test() { await cache.SetStringAsync(\u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;); var value = await cache.GetStringAsync(\u0026#34;key\u0026#34;); } } Using the Redis Distributed Cache Redis is a high-performance, open source distributed cache system. It is an in-memory database that supports a variety of data structures, such as strings, lists, hash tables, and sets. Redis is designed to provide a high-performance, low-latency distributed cache system that supports large-scale data storage and access while offering high availability and scalability. Redis uses the RESP protocol and is written in C, with support for many languages such as C, C++, C#, Java, Python, and Node.js. In .NET, you can use the open source StackExchange.Redis library to work with the cache.\nRedis for Windows is no longer maintained. You can install Redis using WSL2 or Docker on Windows. Memurai can also be used as a Redis alternative; Memurai is a Redis service for Windows that supports most Redis features and lets you run a Redis service on Windows.\nContainerized Redis Service docker run --name my-redis -d -p 6379:6379 -e REDIS_PASSWORD=guest redis Testing Client Tools Use redis-cli to connect to the Linux cache server and run the following commands for testing.\nredis-cli -h localhost -p 6379 -a Password auth Password set key value get key Using the StackExchange.Redis Client donet add package StackExchange.Redis using StackExchange.Redis; var redis = ConnectionMultiplexer.Connect(\u0026#34;localhost:6379,password=guest\u0026#34;); var db = redis.GetDatabase(); db.StringSet(\u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;); var value = db.StringGet(\u0026#34;key\u0026#34;); Using the IDistributedCache Interface dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis services.AddStackExchangeRedisCache(options =\u0026gt; { options.Configuration = \u0026#34;localhost:6379,password=guest\u0026#34;; options.InstanceName = \u0026#34;SampleInstance\u0026#34;; }); using Microsoft.Extensions.Caching.Distributed; public class MyService(IDistributedCache cache) : IService { public async Task Test() { await cache.SetStringAsync(\u0026#34;key\u0026#34;, \u0026#34;value\u0026#34;); var value = await cache.GetStringAsync(\u0026#34;key\u0026#34;); } } Using the IDistributedCache interface is recommended. It is an abstract interface that makes it easy to switch between different cache implementations such as Redis, Memcached, or Garnet, and it supports the ASP.NET Core caching middleware, making it convenient to use caching in ASP.NET Core.\nUsing Microsoft\u0026rsquo;s Garnet Distributed Cache Garnet is an open source distributed cache system from Microsoft. It is a high-performance, low-latency distributed cache that supports a variety of data structures, such as strings, lists, hash tables, and sets. Garnet is designed to provide a high-performance, low-latency distributed cache system that supports large-scale data storage and access while offering high availability and scalability.\nGarnet uses the popular RESP protocol and is written in C#. It supports most Redis commands while providing additional features such as cluster setup, data persistence, data replication, and data sharding. In C#, you can use the open source StackExchange.Redis library to work with the cache.\nhttps://microsoft.github.io/garnet/docs\nSelf-Hosting in a .NET Program dotnet add package Microsoft.Garnet using Garnet; try { using var server = new GarnetServer(args); server.Start(); Thread.Sleep(Timeout.Infinite); } catch (Exception ex) { Console.WriteLine($\u0026#34;Unable to initialize server due to exception: {ex.Message}\u0026#34;); } Running in a Docker Container docker run --name garnet -d -p 6379:6379 --ulimit memlock=-1 ghcr.io/microsoft/garnet --auth Password --password guest Running as a Windows Service dotnet add package Microsoft.Garnet dotnet add package Microsoft.Extensions.Hosting.WindowsServices public class GarnetService(ILogger\u0026lt;GarnetService\u0026gt; logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (!stoppingToken.IsCancellationRequested) { string[] commandLineArgs = Environment.GetCommandLineArgs(); using var server = new GarnetServer(commandLineArgs); logger.LogInformation(\u0026#34;Starting Garnet server...\u0026#34;); try { server.Start(); } catch (Exception ex) { logger.LogError(ex, \u0026#34;An error occurred while starting the Garnet server.\u0026#34;); } logger.LogInformation(\u0026#34;Garnet server started.\u0026#34;); await Task.Delay(Timeout.Infinite, stoppingToken); } } } var builder = Host.CreateApplicationBuilder(args); builder.Services.AddWindowsService(options =\u0026gt; { options.ServiceName = \u0026#34;Garnet Service\u0026#34;; }); builder.Services.AddHostedService\u0026lt;GarnetService\u0026gt;(); var host = builder.Build(); host.Run(); sc.exe create GarnetService binpath= \u0026#34;C:\\Users\\GarnetWindowsService.exe\u0026#34; start= auto sc.exe start GarnetService sc.exe stop GarnetService sc.exe delete GarnetService Using a Distributed Cache in the Basket Service dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis builder.Services.AddStackExchangeRedisCache(options =\u0026gt; { options.Configuration = builder.Configuration.GetConnectionString(\u0026#34;MyRedisConStr\u0026#34;); }); High-Availability Distributed Cache Clusters Building a high-availability cluster with multiple cache servers can improve the availability and reliability of the system.\nhttps://microsoft.github.io/garnet/docs/cluster/overview\nhttps://redis.io/topics/cluster-tutorial\nservices.AddStackExchangeRedisCache(options =\u0026gt; { options.Configuration = \u0026#34;localhost:6379,localhost:6380,localhost:6381\u0026#34;; options.InstanceName = \u0026#34;SampleInstance\u0026#34;; }); services.AddStackExchangeRedisCache(options =\u0026gt; { options.InstanceName = \u0026#34;SampleInstance\u0026#34;; options.ConfigurationOptions = new ConfigurationOptions { Password = \u0026#34;Password\u0026#34;, AbortOnConnectFail = false, EndPoints = { { \u0026#34;localhost\u0026#34;, 6379 }, { \u0026#34;localhost\u0026#34;, 6380 }, { \u0026#34;localhost\u0026#34;, 6381 } } }; }); Caching Best Practices https://learn.microsoft.com/zh-cn/azure/architecture/best-practices/caching\nhttps://learn.microsoft.com/zh-cn/azure/architecture/patterns/cache-aside\nCaching Concepts Cache avalanche, cache penetration, cache breakdown, cache warm-up, cache update, cache degradation, cache hit rate, sliding expiration, absolute expiration, no expiration, and cache dependencies.\nhttps://developer.aliyun.com/article/841392\nIDistributedCache expiration times fall into two kinds: absolute expiration and sliding expiration.\nDistributed Cache Monitoring Tools The RedisInsight tool can be used to monitor a Redis distributed cache system.\nhttps://redis.io/docs/latest/operate/redisinsight/install\ndocker run -d --name redisinsight -p 5540:5540 redis/redisinsight:latest Using the Aspire Component Library for Distributed Caching Aspire Host Project Configuration dotnet add package Aspire.Hosting.Redis var redis = builder.AddRedis(\u0026#34;cache\u0026#34;); builder.AddProject\u0026lt;Projects.ExampleProject\u0026gt;().WithReference(redis) Microservice Project Configuration dotnet add package Aspire.StackExchange.Redis.DistributedCaching builder.AddRedisDistributedCache(\u0026#34;cache\u0026#34;); ","permalink":"/en/helloshop/distributed-cache/","summary":"\u003cp\u003eCaching is a common technique aimed at improving the performance and scalability of a system. To cache data, frequently accessed data is temporarily copied into fast storage located close to the application. If this fast data store is closer to the application than the original source, caching can dramatically improve the response time of client applications by serving data more quickly.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"cache-aside\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/cache-aside.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"cache-types\"\u003eCache Types\u003c/h2\u003e\n\u003ch3 id=\"private-in-memory-cache\"\u003ePrivate In-Memory Cache\u003c/h3\u003e\n\u003cp\u003eA private cache means the caching service runs on the same server as the application; it is part of the application, usually a library or module. A private cache is typically an in-memory cache, such as \u003ccode\u003eMemoryCache\u003c/code\u003e or \u003ccode\u003eConcurrentDictionary\u003c/code\u003e. It is a lightweight cache suitable for small applications. The advantage of a private cache is that it is simple and easy to use; the drawback is that data cannot be shared across servers, making it unsuitable for large applications. The IMemoryCache interface can be used to work with a private cache in .NET.\u003c/p\u003e","title":"Improving System Performance with Caching"},{"content":"System.TimeProvider is an abstraction of time that provides a point in time as a DateTimeOffset value. By using TimeProvider, you can make sure your code is testable and predictable. TimeProvider was introduced in .NET 8.\nDefault Implementation By default, TimeProvider uses DateTimeOffset.UtcNow as its time source.\nConsole.WriteLine($\u0026#34;Local: {TimeProvider.System.GetLocalNow()}\u0026#34;); Console.WriteLine($\u0026#34;Utc: {TimeProvider.System.GetUtcNow()}\u0026#34;); Custom Implementation public class CustomTimeProvider: TimeProvider { public override DateTimeOffset GetUtcNow() =\u0026gt; DateTimeOffset.UtcNow.AddHours(1); } builder.Services.AddSingleton(TimeProvider.System); builder.Services.AddSingleton\u0026lt;TimeProvider, CustomTimeProvider\u0026gt;(); public class MyService { private readonly TimeProvider _timeProvider; public MyService(TimeProvider timeProvider) { _timeProvider = timeProvider; } public void DoSomething() { var now = _timeProvider.Now; Console.WriteLine(now); } } FakeTimeProvider Implementation dotnet add package Microsoft.Extensions.TimeProvider.Testing FakeTimeProvider fakeTimeProvider = new(); fakeTimeProvider.SetUtcNow(fakeTimeProvider.GetUtcNow().AddHours(1)); MyService service = new (timeProvider); service.DoSomething(); ","permalink":"/en/helloshop/time-provider/","summary":"\u003cp\u003eSystem.TimeProvider is an abstraction of time that provides a point in time as a DateTimeOffset value. By using TimeProvider, you can make sure your code is testable and predictable. TimeProvider was introduced in .NET 8.\u003c/p\u003e\n\u003ch2 id=\"default-implementation\"\u003eDefault Implementation\u003c/h2\u003e\n\u003cp\u003eBy default, TimeProvider uses DateTimeOffset.UtcNow as its time source.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e$\u0026#34;Local: {TimeProvider.System.GetLocalNow()}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e$\u0026#34;Utc:   {TimeProvider.System.GetUtcNow()}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"custom-implementation\"\u003eCustom Implementation\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCustomTimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eTimeProvider\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003eoverride\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e \u003cspan class=\"n\"\u003eGetUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddHours\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddSingleton\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSystem\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddSingleton\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eTimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCustomTimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eMyService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"n\"\u003eTimeProvider\u003c/span\u003e \u003cspan class=\"n\"\u003e_timeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eMyService\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTimeProvider\u003c/span\u003e \u003cspan class=\"n\"\u003etimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_timeProvider\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eDoSomething\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003enow\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_timeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enow\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"faketimeprovider-implementation\"\u003eFakeTimeProvider Implementation\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edotnet\u003c/span\u003e \u003cspan class=\"n\"\u003eadd\u003c/span\u003e \u003cspan class=\"n\"\u003epackage\u003c/span\u003e \u003cspan class=\"n\"\u003eMicrosoft\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"py\"\u003eExtensions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"py\"\u003eTimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"py\"\u003eTesting\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eFakeTimeProvider\u003c/span\u003e \u003cspan class=\"n\"\u003efakeTimeProvider\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003efakeTimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efakeTimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eAddHours\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eMyService\u003c/span\u003e \u003cspan class=\"n\"\u003eservice\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etimeProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDoSomething\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Injecting Time with the TimeProvider Class"},{"content":"Add the PostgreSQL database to the Host project dotnet add package Aspire.Hosting.PostgreSQL var builder = DistributedApplication.CreateBuilder(args); var postgres = builder.AddPostgres(\u0026#34;postgres\u0026#34;); var postgresdb = postgres.AddDatabase(\u0026#34;postgresdb\u0026#34;); var exampleProject = builder.AddProject\u0026lt;Projects.ExampleProject\u0026gt;().WithReference(postgresdb); Add PostgreSQL pgAdmin resource var postgres = builder.AddPostgres(\u0026#34;postgres\u0026#34;).WithPgAdmin(); Add PostgreSQL pgWeb resource var postgres = builder.AddPostgres(\u0026#34;postgres\u0026#34;).WithPgWeb(); Reference the PostgreSQL database in the final application dotnet add package Aspire.Npgsql.EntityFrameworkCore.PostgreSQL builder.AddNpgsqlDbContext\u0026lt;IdentityServiceDbContext\u0026gt;(connectionName: DbConstants.ConnectionStringName, configureDbContextOptions: options =\u0026gt; { new NpgsqlDbContextOptionsBuilder(options).MigrationsHistoryTable(DbConstants.MigrationsHistoryTableName); }); ","permalink":"/en/helloshop/aspire-postgresql/","summary":"\u003ch2 id=\"add-the-postgresql-database-to-the-host-project\"\u003eAdd the PostgreSQL database to the Host project\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Aspire.Hosting.PostgreSQL\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDistributedApplication\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epostgres\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddPostgres\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;postgres\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epostgresdb\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epostgres\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddDatabase\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;postgresdb\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eexampleProject\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddProject\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProjects\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eExampleProject\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;().\u003c/span\u003e\u003cspan class=\"n\"\u003eWithReference\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epostgresdb\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"add-postgresql-pgadmin-resource\"\u003eAdd PostgreSQL pgAdmin resource\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epostgres\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddPostgres\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;postgres\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eWithPgAdmin\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"add-postgresql-pgweb-resource\"\u003eAdd PostgreSQL pgWeb resource\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003epostgres\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddPostgres\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;postgres\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eWithPgWeb\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"reference-the-postgresql-database-in-the-final-application\"\u003eReference the PostgreSQL database in the final application\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Aspire.Npgsql.EntityFrameworkCore.PostgreSQL\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddNpgsqlDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003econnectionName\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eDbConstants\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConnectionStringName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003econfigureDbContextOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eNpgsqlDbContextOptionsBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eMigrationsHistoryTable\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDbConstants\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMigrationsHistoryTableName\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Integrating PostgreSQL database in Aspire"},{"content":"Why is PostgreSQL considered an advanced relational database? Powerful features: PostgreSQL supports advanced capabilities such as complex queries, triggers, and MVCC. Strong consistency: Its concurrency model helps keep data consistent under high load. Great scalability: It supports both vertical and horizontal growth scenarios. Security: It provides access control and encryption options. Backup and recovery: It includes robust backup and recovery mechanisms. Free and open source: You can use, modify, and distribute it freely. Install PostgreSQL and pgAdmin There are multiple installation options. A simple setup is to install PostgreSQL with pgAdmin, then use pgAdmin for management and remote connections.\nConnect to PostgreSQL from Visual Studio Code You can connect using Microsoft\u0026rsquo;s PostgreSQL extension or third-party extensions.\nUse AI assistants in VS Code to write SQL GitHub Copilot can help generate SQL based on context and improve efficiency.\nMigrate SQL Server to PostgreSQL You can use SQL Server Import and Export Wizard. See Microsoft docs:\nConnect to a PostgreSQL data source (SQL Server Import and Export Wizard)\nUse EF Core with PostgreSQL Entity Framework Core supports PostgreSQL via provider packages.\nusing System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; namespace EFCoreDemo { public class Program { public static void Main(string[] args) { using (var db = new BloggingContext()) { db.Blogs.Add(new Blog { Url = \u0026#34;http://blogs.msdn.com/adonet\u0026#34; }); var count = db.SaveChanges(); Console.WriteLine(\u0026#34;{0} records saved to database\u0026#34;, count); Console.WriteLine(); Console.WriteLine(\u0026#34;All blogs in database:\u0026#34;); foreach (var blog in db.Blogs) { Console.WriteLine(\u0026#34; - {0}\u0026#34;, blog.Url); } } } } public class BloggingContext : DbContext { public DbSet\u0026lt;Blog\u0026gt; Blogs { get; set; } public DbSet\u0026lt;Post\u0026gt; Posts { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(@\u0026#34;Host=localhost;Database=postgres;Username=postgres;Password=123456\u0026#34;); } } public class Blog { public int BlogId { get; set; } public string Url { get; set; } public List\u0026lt;Post\u0026gt; Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } } } ","permalink":"/en/database/postgre-sql/","summary":"\u003ch2 id=\"why-is-postgresql-considered-an-advanced-relational-database\"\u003eWhy is PostgreSQL considered an advanced relational database?\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003ePowerful features: PostgreSQL supports advanced capabilities such as complex queries, triggers, and MVCC.\u003c/li\u003e\n\u003cli\u003eStrong consistency: Its concurrency model helps keep data consistent under high load.\u003c/li\u003e\n\u003cli\u003eGreat scalability: It supports both vertical and horizontal growth scenarios.\u003c/li\u003e\n\u003cli\u003eSecurity: It provides access control and encryption options.\u003c/li\u003e\n\u003cli\u003eBackup and recovery: It includes robust backup and recovery mechanisms.\u003c/li\u003e\n\u003cli\u003eFree and open source: You can use, modify, and distribute it freely.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"install-postgresql-and-pgadmin\"\u003eInstall PostgreSQL and pgAdmin\u003c/h2\u003e\n\u003cp\u003eThere are multiple installation options. A simple setup is to install PostgreSQL with pgAdmin, then use pgAdmin for management and remote connections.\u003c/p\u003e","title":"Learn and Use PostgreSQL"},{"content":"Load testing Test whether the app can handle a specified user load under specific conditions while still meeting the response goals. The app runs in a normal state.\nStress testing Test the app\u0026rsquo;s stability when running under extreme conditions, usually for a long period. The test places high user load on the app (spike or gradually increasing load) or restricts the app\u0026rsquo;s compute resources. Stress testing determines whether the app under stress can recover from failure and correctly return to the expected behavior. Under stress, the app runs under unusually high pressure.\nTesting tools Cloud-based testing and Apache JMeter are two popular load testing tools. Load and stress tests should be performed in release and production modes, not in debug and development modes.\nUsing Apache JMeter Apache JMeter is an open-source Java application for load testing, functional testing, and performance testing. JMeter can simulate many kinds of load, including load testing, stress testing, functional testing, benchmark testing, and more.\nIt provides a powerful graphical interface where you can build a test plan and then execute it. JMeter supports many protocols, including HTTP, HTTPS, FTP, JMS, SOAP, TCP, and LDAP, and additional protocols can be supported through plugins.\nhttps://jmeter.apache.org/usermanual/build-web-test-plan.html\nBenchmark testing Benchmark testing is a testing method used to establish an application\u0026rsquo;s performance baseline so that performance can be optimized over the application\u0026rsquo;s lifecycle. BenchmarkDotNet can be used to run benchmarks in .NET applications.\ndotnet add package BenchmarkDotNet public class Md5VsSha256 { [Benchmark] public byte[] Sha256() =\u0026gt; sha256.ComputeHash(data); [Benchmark] public byte[] Md5() =\u0026gt; md5.ComputeHash(data); } public class Program { public static void Main(string[] args) { BenchmarkRunner.Run\u0026lt;Md5VsSha256\u0026gt;(); } } ","permalink":"/en/helloshop/load-tests/","summary":"\u003ch2 id=\"load-testing\"\u003eLoad testing\u003c/h2\u003e\n\u003cp\u003eTest whether the app can handle a specified user load under specific conditions while still meeting the response goals. The app runs in a normal state.\u003c/p\u003e\n\u003ch2 id=\"stress-testing\"\u003eStress testing\u003c/h2\u003e\n\u003cp\u003eTest the app\u0026rsquo;s stability when running under extreme conditions, usually for a long period. The test places high user load on the app (spike or gradually increasing load) or restricts the app\u0026rsquo;s compute resources. Stress testing determines whether the app under stress can recover from failure and correctly return to the expected behavior. Under stress, the app runs under unusually high pressure.\u003c/p\u003e","title":"Load Testing and Stress Testing"},{"content":"Azure Data Studio is a lightweight, cross-platform database tool with a modern user interface that helps users manage databases more easily.\nFully Open Source Azure Data Studio is an open source project with all of its code hosted on GitHub. Users are free to view the source code and can also contribute to the project.\nhttps://github.com/Microsoft/azuredatastudio\nManageable Databases Azure Data Studio supports a variety of databases, including SQL Server, PostgreSQL, MySQL, TimescaleDB, Oracle, and more. By installing the corresponding extensions, it can support even more databases.\nQuery Editor with IntelliSense A rich SQL editor, IntelliSense, keyword completion, code snippets, code navigation, and source control integration (Git) provide a modern, keyboard-focused SQL coding experience.\nSmart SQL Code Snippets Azure Data Studio provides a large number of SQL code snippets to help users write SQL code quickly.\nUsing Server Groups With server groups, you can organize servers into categories, making it easier to manage multiple servers.\nUsing the Integrated Terminal Common command-line tools, such as the sqlcmd command, can be used in the \u0026ldquo;Integrated Terminal\u0026rdquo; window of the user interface.\nUsing the Table Designer Azure Data Studio provides a table designer that helps users design tables more easily.\nBacking Up and Restoring Databases Azure Data Studio provides backup and restore functionality, helping users back up and restore databases more easily.\nUsing the Copilot Extension The Copilot extension leverages powerful AI to help users write SQL code more easily and improve coding efficiency.\nManaging SQL Scripts with Git Azure Data Studio integrates Git, helping users manage SQL scripts more easily.\nViewing Query Plans Capturing both estimated and actual query plans is supported. A query plan provides a graphical representation of the data retrieval (or modification) methods chosen by the SQL Server query optimizer.\nExtensibility Extensions can be installed online or manually using VSIX files, and extensions can provide additional functionality.\nSome Useful Extensions SQL Server Import (mssql) - import data SQL Server Profiler (mssql) - analyze SQL Server database performance SQL Server Agent (mssql) - manage SQL Server Agent Compared with SQL Server Management Studio Azure Data Studio is a lightweight, cross-platform database tool. Compared with SQL Server Management Studio, Azure Data Studio is lighter and better suited for platforms such as Mac and Linux. It also supports multiple databases, helping users manage databases more easily, and its functionality can be extended on demand through extensions, making it more flexible.\n","permalink":"/en/helloshop/azure-data-studio/","summary":"\u003cp\u003eAzure Data Studio is a lightweight, cross-platform database tool with a modern user interface that helps users manage databases more easily.\u003c/p\u003e\n\u003ch2 id=\"fully-open-source\"\u003eFully Open Source\u003c/h2\u003e\n\u003cp\u003eAzure Data Studio is an open source project with all of its code hosted on GitHub. Users are free to view the source code and can also contribute to the project.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/Microsoft/azuredatastudio\"\u003ehttps://github.com/Microsoft/azuredatastudio\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"manageable-databases\"\u003eManageable Databases\u003c/h2\u003e\n\u003cp\u003eAzure Data Studio supports a variety of databases, including SQL Server, PostgreSQL, MySQL, TimescaleDB, Oracle, and more. By installing the corresponding extensions, it can support even more databases.\u003c/p\u003e","title":"Managing Databases with Azure Data Studio"},{"content":"Model binding best practices An entity object is an EF concept; each entity object maps to a table in the database. A model object is an MVC concept; it is the data structure of HTTP requests and responses. HTTP requests carry data through URL parameters, forms, headers, JSON payloads, and so on. This data is ultimately bound into model objects, and after the model is converted to an entity object, it is persisted to the database.\npublic class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public class ProductModel { public string Name { get; set; } public decimal Price { get; set; } } public class ProductController : Controller { private readonly ShopDbContext _context; public ProductController(ShopDbContext context) { _context = context; } [HttpPost] public async Task\u0026lt;IActionResult\u0026gt; Create(ProductModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var product = new Product { Name = model.Name, Price = model.Price }; _context.Products.Add(product); await _context.SaveChangesAsync(); return CreatedAtAction(nameof(Get), new { id = product.Id }, product); } } Different HTTP operations should use different model objects, such as a create model, an update model, a query model, and a delete model. The benefit is a clearer separation of responsibilities: the create model only contains the fields needed for creation, the update model only the fields needed for updating, the query model only the fields needed for querying, and the delete model only the fields needed for deletion. With single responsibilities, maintainability improves, and each model can also provide its own validation rules.\n","permalink":"/en/helloshop/model-binding/","summary":"\u003ch2 id=\"model-binding-best-practices\"\u003eModel binding best practices\u003c/h2\u003e\n\u003cp\u003eAn entity object is an EF concept; each entity object maps to a table in the database. A model object is an MVC concept; it is the data structure of HTTP requests and responses. HTTP requests carry data through URL parameters, forms, headers, JSON payloads, and so on. This data is ultimately bound into model objects, and after the model is converted to an entity object, it is persisted to the database.\u003c/p\u003e","title":"Model Binding"},{"content":"What Is a Multi-Tenant Application A multi-tenant application is a software architecture design that allows a single instance of software to serve multiple customers. Each customer is called a tenant; data across tenants is automatically isolated, so one tenant\u0026rsquo;s data never affects another\u0026rsquo;s.\nSearch for \u0026ldquo;multi-tenancy\u0026rdquo; on the ZeroDegree Programming website to watch the multi-tenant design video tutorial.\nIsolating Tenant Data with a Shared Table and a Discriminator Column The shared-table multi-tenant design stores data for multiple tenants in a single table, distinguishing each tenant\u0026rsquo;s rows with an added TenantId column.\nQuerying Multi-Tenant Data public class FieldIsolationServiceDbContext(DbContextOptions\u0026lt;FieldIsolationServiceDbContext\u0026gt; options, ICurrentTenant currentTenant) : DbContext(options) { protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity\u0026lt;Product\u0026gt;().Property(p =\u0026gt; p.Name).HasMaxLength(32); foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes()) { if (entityType.ClrType.IsAssignableTo(typeof(IMultiTenant))) { modelBuilder.Entity(entityType.ClrType).AddQueryFilter\u0026lt;IMultiTenant\u0026gt;(e =\u0026gt; e.TenantId == currentTenant.TenantId); } } base.OnModelCreating(modelBuilder); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.AddInterceptors(new TenantSaveChangesInterceptor(currentTenant)); base.OnConfiguring(optionsBuilder); } } Setting the Tenant ID Automatically When Saving Data private void MultiTenancyTracking(DbContext dbContext) { IEnumerable\u0026lt;EntityEntry\u0026lt;IMultiTenant\u0026gt;\u0026gt; multiTenancyEntries = dbContext.ChangeTracker.Entries\u0026lt;IMultiTenant\u0026gt;().Where(entry =\u0026gt; entry.State == EntityState.Added || entry.State == EntityState.Modified); multiTenancyEntries?.ToList().ForEach(entityEntry =\u0026gt; { entityEntry.Entity.TenantId ??= currentTenant.TenantId; }); } Isolating Tenant Data with Multiple Databases The multi-database multi-tenant design creates a separate database for each tenant, distinguishing tenant data by the database connection string.\nprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (currentTenant.TenantId != null) { string? connectionString = configuration.GetConnectionString(currentTenant.TenantId); optionsBuilder.UseNpgsql(connectionString); } base.OnConfiguring(optionsBuilder); } ZeroFramework TenantDbConnectionInterceptor\nSchema-Based Multi-Tenant Design A schema-based multi-tenant design creates a separate schema for each tenant, distinguishing tenant data by schema.\nprotected override void OnModelCreating(ModelBuilder modelBuilder) { if (currentTenant.TenantId != null) { modelBuilder.HasDefaultSchema(currentTenant.TenantId); } base.OnModelCreating(modelBuilder); } Currently EF Core does not create schemas automatically during migrations, so schemas must be created manually.\nImplementing Multi-Tenancy in ZeroFramework Design a tenants table, include the tenant ID in the login token, extract the tenant ID from the token on every request, look up the tenant information by tenant ID, store the tenant information in the current request\u0026rsquo;s context, and use it to isolate tenant data.\n","permalink":"/en/helloshop/multi-tenancy/","summary":"\u003ch2 id=\"what-is-a-multi-tenant-application\"\u003eWhat Is a Multi-Tenant Application\u003c/h2\u003e\n\u003cp\u003eA multi-tenant application is a software architecture design that allows a single instance of software to serve multiple customers. Each customer is called a tenant; data across tenants is automatically isolated, so one tenant\u0026rsquo;s data never affects another\u0026rsquo;s.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"multi-tenancy\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/multi-tenancy.svg\"\u003e\u003c/p\u003e\n\u003cp\u003eSearch for \u0026ldquo;multi-tenancy\u0026rdquo; on the ZeroDegree Programming website to watch the multi-tenant design video tutorial.\u003c/p\u003e\n\u003ch2 id=\"isolating-tenant-data-with-a-shared-table-and-a-discriminator-column\"\u003eIsolating Tenant Data with a Shared Table and a Discriminator Column\u003c/h2\u003e\n\u003cp\u003eThe shared-table multi-tenant design stores data for multiple tenants in a single table, distinguishing each tenant\u0026rsquo;s rows with an added TenantId column.\u003c/p\u003e","title":"Multi-Tenant Application Design"},{"content":"Adding Application Components via Add Methods Using methods such as AddProject, AddContainer, AddExecutable, AddParameter, and AddConnectionString, you can orchestrate the app host into a complete application.\nConfiguring Explicit Resource Startup builder.AddProject\u0026lt;Projects.MyApp\u0026gt;(\u0026#34;dbmigration\u0026#34;).WithExplicitStart(); Using the WithExplicitStart method, you can configure explicit resource startup; resources configured for explicit startup need to be started manually.\nReferencing Resources with the WithReference Method builder.AddProject\u0026lt;Projects.MyApp\u0026gt;(\u0026#34;myapp\u0026#34;).WithReference(postgresdb); The referenced resource can be an endpoint, a connection string, or another resource. The referenced resource is passed to the referencing resource via environment variables.\nWaiting for Resources to Start and Complete builder.AddProject\u0026lt;Projects.MyApp\u0026gt;(\u0026#34;myapp\u0026#34;).WithWaitFor(postgresdb); builder.AddProject\u0026lt;Projects.MyApp\u0026gt;(\u0026#34;myapp\u0026#34;).WithWaitForCompletion(\u0026#34;dbmigration\u0026#34;); Customizing Container Startup Arguments var cache = builder.AddRedis(\u0026#34;cache\u0026#34;).WithImageTag(\u0026#34;latest\u0026#34;); Container Resource Lifetime var cache1 = builder.AddRedis(\u0026#34;cache1\u0026#34;).WithLifetime(ContainerLifetime.Persistent); var cache2 = builder.AddRedis(\u0026#34;cache1\u0026#34;).WithLifetime(ContainerLifetime.Session); Persistent means the container resource has a persistent lifetime, while Session means the container resource has a session lifetime.\nExternal Parameters Add the following node to the appsettings.json configuration file in the Host project.\n{ \u0026#34;Parameters\u0026#34;: { \u0026#34;myvalue\u0026#34;: \u0026#34;local-value\u0026#34; } } var value1 = builder.AddParameter(\u0026#34;myvalue\u0026#34;); var value2 = builder.AddParameter(\u0026#34;myvalue\u0026#34;, secret: true); builder.AddProject\u0026lt;Projects.ApiService\u0026gt;(\u0026#34;api\u0026#34;).WithEnvironment(\u0026#34;EXAMPLE_VALUE\u0026#34;, value1); External Connection Strings Add the following node to the appsettings.json configuration file in the Host project.\n{ \u0026#34;ConnectionStrings\u0026#34;: { \u0026#34;myconnection\u0026#34;: \u0026#34;Server=myserver;Database=mydb;User=myuser;Password=mypassword;\u0026#34; } } var connection = builder.AddConnectionString(\u0026#34;myconnection\u0026#34;); builder.AddProject\u0026lt;Projects.ApiService\u0026gt;(\u0026#34;api\u0026#34;).WithReference(connection); Endpoint References var builder = DistributedApplication.CreateBuilder(args); var customContainer = builder.AddContainer(\u0026#34;myapp\u0026#34;, \u0026#34;mycustomcontainer\u0026#34;).WithHttpEndpoint(port: 9043, name: \u0026#34;endpoint\u0026#34;); var endpoint = customContainer.GetEndpoint(\u0026#34;endpoint\u0026#34;); var apiservice = builder.AddProject\u0026lt;Projects.AspireApp_ApiService\u0026gt;(\u0026#34;apiservice\u0026#34;).WithReference(endpoint); Enabling Management Extensions on the Redis Component var redis = builder.AddRedis(\u0026#34;redis\u0026#34;).WithRedisInsight(); var redis = builder.AddRedis(\u0026#34;redis\u0026#34;).WithRedisCommander(); Registering Lifecycle Hooks using Aspire.Hosting.Lifecycle; using Microsoft.Extensions.Logging; var builder = DistributedApplication.CreateBuilder(args); builder.Services.AddLifecycleHook\u0026lt;LifecycleLogger\u0026gt;(); builder.Build().Run(); class LifecycleLogger(ILogger\u0026lt;LifecycleLogger\u0026gt; logger): IDistributedApplicationLifecycleHook { public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default) { logger.LogInformation(\u0026#34;BeforeStartAsync\u0026#34;); return Task.CompletedTask; } public Task AfterEndpointsAllocatedAsync( DistributedApplicationModel appModel, CancellationToken cancellationToken = default) { logger.LogInformation(\u0026#34;AfterEndpointsAllocatedAsync\u0026#34;); return Task.CompletedTask; } public Task AfterResourcesCreatedAsync( DistributedApplicationModel appModel, CancellationToken cancellationToken = default) { logger.LogInformation(\u0026#34;AfterResourcesCreatedAsync\u0026#34;); return Task.CompletedTask; } } Networking Model and Multiple Replicas builder.AddProject\u0026lt;Projects.Networking_Frontend\u0026gt;(\u0026#34;frontend\u0026#34;) .WithHttpEndpoint(port: 5066, IsExternal: true, IsProxied: true) .WithReplicas(2); The IsExternal and IsProxied properties determine how an endpoint is managed and exposed. When IsExternal is true, the endpoint is exposed publicly; when IsProxied is true, the endpoint is proxied.\nhttps://learn.microsoft.com/zh-cn/dotnet/aspire/fundamentals/networking-overview\nOther Features Custom resource commands Adding Dockerfiles to the .NET app model Capturing events in Aspire References https://learn.microsoft.com/zh-cn/dotnet/aspire\n","permalink":"/en/helloshop/aspire-host-overview/","summary":"\u003ch2 id=\"adding-application-components-via-add-methods\"\u003eAdding Application Components via Add Methods\u003c/h2\u003e\n\u003cp\u003eUsing methods such as AddProject, AddContainer, AddExecutable, AddParameter, and AddConnectionString, you can orchestrate the app host into a complete application.\u003c/p\u003e\n\u003ch2 id=\"configuring-explicit-resource-startup\"\u003eConfiguring Explicit Resource Startup\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddProject\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProjects\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMyApp\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;dbmigration\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eWithExplicitStart\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing the WithExplicitStart method, you can configure explicit resource startup; resources configured for explicit startup need to be started manually.\u003c/p\u003e\n\u003ch2 id=\"referencing-resources-with-the-withreference-method\"\u003eReferencing Resources with the WithReference Method\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddProject\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eProjects\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eMyApp\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;myapp\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eWithReference\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epostgresdb\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe referenced resource can be an endpoint, a connection string, or another resource. The referenced resource is passed to the referencing resource via environment variables.\u003c/p\u003e","title":"Orchestration with the Aspire App Host"},{"content":"The ordering part covers many technical topics, including the CQRS pattern, MediatR for local command dispatch, local event handling, abstraction and implementation of distributed events, the outbox pattern, distributed locks, and more.\nOrdering Service Architecture Diagram Microservice Communication Client Side The client side mainly covers order creation, payment, cancellation, and querying. The client calls the ordering microservice through the API Gateway; the ordering microservice processes commands via MediatR, then publishes events through the event bus; the event bus dispatches the events to event handlers, which process them and then persist the events to the database.\nOrdering WebApi WebApi serves as the entry point of the ordering microservice. It receives client requests, processes commands via MediatR, and then publishes events through the event bus.\nQueries Queries are handled by a read replica, which is an independent database dedicated to queries. Data in the read replica is synchronized from the primary database via streaming replication, is read-only, and is eventually consistent.\nCommands The command pattern is used to handle order creation, payment, cancellation, and other commands. The command pattern is a behavioral design pattern, and command data is ultimately persisted to the primary database.\nLocal Events Local events are events internal to a microservice. They are published via MediatR and can only be used within the current microservice.\nDistributed Events Distributed events are events exchanged between microservices. They are published through the event bus and can be used across multiple microservices; publishing and handling are asynchronous. Dapr\u0026rsquo;s Pub/Sub component implements event publishing and subscription, and many kinds of message queues can be used underneath.\nUsing Distributed Locks Distributed locks handle concurrency problems. They are implemented through a Dapr component. A distributed lock is a synchronization tool in a distributed system for solving concurrency issues; currently Dapr\u0026rsquo;s distributed lock is built on Redis.\n","permalink":"/en/helloshop/ordering-architecture/","summary":"\u003cp\u003eThe ordering part covers many technical topics, including the CQRS pattern, MediatR for local command dispatch, local event handling, abstraction and implementation of distributed events, the outbox pattern, distributed locks, and more.\u003c/p\u003e\n\u003ch2 id=\"ordering-service-architecture-diagram\"\u003eOrdering Service Architecture Diagram\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Basic system architecture\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/ordering-architecture.svg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"microservice-communication\"\u003eMicroservice Communication\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Microservice communication\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/ordering-sequence.svg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"client-side\"\u003eClient Side\u003c/h2\u003e\n\u003cp\u003eThe client side mainly covers order creation, payment, cancellation, and querying. The client calls the ordering microservice through the API Gateway; the ordering microservice processes commands via MediatR, then publishes events through the event bus; the event bus dispatches the events to event handlers, which process them and then persist the events to the database.\u003c/p\u003e","title":"Ordering Microservice Architecture Overview"},{"content":"Library Improvements Numeric Ordering for String Comparison In .NET 10, the System.String class adds a CompareAsNumbers method for comparing strings as numbers. This is very useful for sorting strings that contain numbers, such as file names or version numbers.\nStringComparer numericStringComparer = StringComparer.Create(CultureInfo.CurrentCulture, CompareOptions.NumericOrdering); Console.WriteLine(numericStringComparer.Equals(\u0026#34;02\u0026#34;, \u0026#34;2\u0026#34;)); // Output: True foreach (string os in new[] { \u0026#34;Windows 8\u0026#34;, \u0026#34;Windows 10\u0026#34;, \u0026#34;Windows 11\u0026#34; }.Order(numericStringComparer)) { Console.WriteLine(os); } // Output: // Windows 8 // Windows 10 // Windows 11 HashSet\u0026lt;string\u0026gt; set = new HashSet\u0026lt;string\u0026gt;(numericStringComparer) { \u0026#34;007\u0026#34; }; Console.WriteLine(set.Contains(\u0026#34;7\u0026#34;)); // Output: True UTF-8 Support for Hexadecimal String Conversion .NET 10 adds UTF-8 support for hexadecimal string conversion operations in the Convert class. These new methods provide an efficient way to convert between UTF-8 byte sequences and hexadecimal representations without intermediate string allocations:\nConvert.FromHexString(ReadOnlySpan\u0026lt;Byte\u0026gt;) Convert.FromHexString(ReadOnlySpan\u0026lt;Byte\u0026gt;, Span\u0026lt;Byte\u0026gt;, Int32, Int32) Convert.TryToHexString(ReadOnlySpan\u0026lt;Byte\u0026gt;, Span\u0026lt;Byte\u0026gt;, Int32) Convert.TryToHexStringLower(ReadOnlySpan\u0026lt;Byte\u0026gt;, Span\u0026lt;Byte\u0026gt;, Int32) Convert.TryToHexStringUpper(ReadOnlySpan\u0026lt;Byte\u0026gt;, Span\u0026lt;Byte\u0026gt;, Int32) Option to Disallow Duplicate JSON Properties The JSON specification does not define how to handle duplicate properties when deserializing a JSON payload. This can lead to unexpected results and security vulnerabilities. .NET 10 introduces the JsonSerializerOptions.AllowDuplicateProperties option to disallow duplicate JSON properties.\nvar options = new JsonSerializerOptions { AllowDuplicateProperties = false }; string json = @\u0026#34;{ \u0026#34;\u0026#34;Name\u0026#34;\u0026#34;: \u0026#34;\u0026#34;Alice\u0026#34;\u0026#34;, \u0026#34;\u0026#34;Name\u0026#34;\u0026#34;: \u0026#34;\u0026#34;Bob\u0026#34;\u0026#34; }\u0026#34;; try { var person = JsonSerializer.Deserialize\u0026lt;Person\u0026gt;(json, options); } catch (JsonException ex) { Console.WriteLine($\u0026#34;JSON deserialization failed: {ex.Message}\u0026#34;); } SDK Improvements Single Tool Execution You can now run a .NET tool with a command via dotnet tool exec, without installing the tool globally or locally.\ndotnet tool exec dotnetsay \u0026#34;Hello, World!\u0026#34; The New dnx Tool Execution Script The dnx script provides a simplified way to execute tools.\ndnx dotnetsay \u0026#34;Hello, World!\u0026#34; File-Based Applications Console.WriteLine(\u0026#34;Hello, World!\u0026#34;); You can run it by saving the code to a file named app.cs and running the following command:\ndotnet run app.cs #:package Colorful.Console@1.2.15 Colorful.Console.WriteAscii(\u0026#34;Hello, World!\u0026#34;); You can run it by saving the code to a file named app.cs and running the following command:\ndotnet run app.cs See C# preprocessor directives\nMore Consistent Command Order The .NET CLI commands now follow a more consistent ordering pattern. The following table shows the new preferred forms and their aliases:\nNew preferred form Alias dotnet package add dotnet add package dotnet package list dotnet list package dotnet package remove dotnet remove package dotnet reference add dotnet add reference dotnet reference list dotnet list reference dotnet reference remove dotnet remove reference New Features in C# 14 Extension Members Extension members let you add new methods, properties, or events to existing types without modifying the original type definition. This is especially useful for adding functionality to third-party libraries.\nExtension Methods, Supported for a Long Time public static class StringExtensions { public static bool ContainsNumber(this string str) { return str.Any(char.IsDigit); } } The New Extension Member Syntax public static class StringExtensions { // Extension block for string extension(string source) { // Extension property: public bool ContainsNumber =\u0026gt; source.Any(char.IsDigit); // Extension method: public string Repeat(int count) { return string.Concat(Enumerable.Repeat(source, count)); } } // Static extension block for string extension(string) { // Static extension method: public static string Combine(string first, string second, string? separator = null) { return $\u0026#34;{first}{separator}{second}\u0026#34;; } // Static extension property: public static string SpaceChar =\u0026gt; \u0026#34; \u0026#34;; // Static user defined operator: public static string operator * (string str, int count) { return string.Concat(Enumerable.Repeat(str, count)); } } } Usage examples:\nstring text = \u0026#34;Hello\u0026#34;; Console.WriteLine(text.ContainsNumber); // False Console.WriteLine(text.Repeat(3)); // HelloHelloHello string combined = string.Combine(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;, \u0026#34;, \u0026#34;); Console.WriteLine(combined); // Hello, World Console.WriteLine(string.SpaceChar); // \u0026#34; \u0026#34; string repeated = \u0026#34;Hi\u0026#34; * 3; Console.WriteLine(repeated); // HiHiHi The New field Keyword private string _msg; public string Message { get =\u0026gt; _msg; set =\u0026gt; _msg = value ?? throw new ArgumentNullException(nameof(value)); } can be simplified to:\npublic string Message { get =\u0026gt; field; set =\u0026gt; field = value ?? throw new ArgumentNullException(nameof(value)); } Unbound Generic Types and nameof In C# 14, the nameof operator now supports unbound generic types. This allows you to get the name of a generic type without specifying type arguments.\nConsole.WriteLine(nameof(List\u0026lt;\u0026gt;)); // Output: List Console.WriteLine(nameof(List\u0026lt;int\u0026gt;)); // Output: List Console.WriteLine(nameof(Dictionary\u0026lt;,\u0026gt;)); // Output: Dictionary Custom Compound Assignment C# 14 allows you to define compound assignment operators for custom types. This makes the code more concise and improves readability.\npublic class Point(int x, int y) { public int X { get; set; } = x; public int Y { get; set; } = y; public static Point operator +(Point a, Point b) { return new Point(a.X + b.X, a.Y + b.Y); } public static Point operator ++(Point p) { return new Point(p.X + 1, p.Y + 1); } } Usage examples:\nPoint p1 = new Point { X = 1, Y = 2 }; Point p2 = new Point { X = 3, Y = 4 }; Point p3 = p1 + p2; // Output: p3.X = 4, p3.Y = 6 p1++; // Now p1.X = 5, p1.Y = 7 Null-Conditional Assignment Before C# 14, you had to null-check a variable before assigning to a property:\nif (obj != null) // or obj is not null { obj.Property = newValue(); } can be simplified to:\nobj?.Property = newValue(); ","permalink":"/en/dotnet/dotnet-10/","summary":"\u003ch2 id=\"library-improvements\"\u003eLibrary Improvements\u003c/h2\u003e\n\u003ch3 id=\"numeric-ordering-for-string-comparison\"\u003eNumeric Ordering for String Comparison\u003c/h3\u003e\n\u003cp\u003eIn .NET 10, the \u003ccode\u003eSystem.String\u003c/code\u003e class adds a \u003ccode\u003eCompareAsNumbers\u003c/code\u003e method for comparing strings as numbers. This is very useful for sorting strings that contain numbers, such as file names or version numbers.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eStringComparer\u003c/span\u003e \u003cspan class=\"n\"\u003enumericStringComparer\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eStringComparer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCultureInfo\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCurrentCulture\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCompareOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eNumericOrdering\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enumericStringComparer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEquals\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;02\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Output: True\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Windows 8\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Windows 10\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Windows 11\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e}.\u003c/span\u003e\u003cspan class=\"n\"\u003eOrder\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enumericStringComparer\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Output:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Windows 8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Windows 10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Windows 11\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eHashSet\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eHashSet\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003enumericStringComparer\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;007\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eConsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWriteLine\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eContains\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;7\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Output: True\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"utf-8-support-for-hexadecimal-string-conversion\"\u003eUTF-8 Support for Hexadecimal String Conversion\u003c/h3\u003e\n\u003cp\u003e.NET 10 adds UTF-8 support for hexadecimal string conversion operations in the Convert class. These new methods provide an efficient way to convert between UTF-8 byte sequences and hexadecimal representations without intermediate string allocations:\u003c/p\u003e","title":"Overview of New Features in .NET 10"},{"content":"Paging Parameters Requests should use the GET method\nhttp://localhost:8080/api/products?keyword=test\u0026amp;pagenumber=1\u0026amp;pagesize=5\u0026amp;orderby=id desc,price asc The response is as follows\n{ \u0026#34;totalCount\u0026#34;: 100, \u0026#34;items\u0026#34;: [ { \u0026#34;id\u0026#34;: 1, \u0026#34;name\u0026#34;: \u0026#34;test\u0026#34;, \u0026#34;price\u0026#34;: 100 }, { \u0026#34;id\u0026#34;: 2, \u0026#34;name\u0026#34;: \u0026#34;test\u0026#34;, \u0026#34;price\u0026#34;: 200 } ] } Paged Request Model public class PagedAndSortedRequest : PagedRequest { public string? OrderBy { get; init; } } Paged Response Model public class PagedResponse\u0026lt;T\u0026gt;(IReadOnlyList\u0026lt;T\u0026gt; items, int totalCount) { public IReadOnlyList\u0026lt;T\u0026gt; Items { get; init; } = items; public int TotalCount { get; init; } = totalCount; } Extending IQueryable to Sort by Property Name QueryableOrderByExtensions.cs Convert a string condition into a sort expression\nIOrderedQueryable\u0026lt;TSource\u0026gt; OrderBy(IQueryable\u0026lt;TSource\u0026gt; source, string propertyName) Extending IQueryable for Searching and Sorting QueryableExtensions.cs Extension methods for paging and sorting\nIQueryable\u0026lt;TEntity\u0026gt; SortBy\u0026lt;TEntity\u0026gt;(this IQueryable\u0026lt;TEntity\u0026gt; query, string? orderBy = null); IQueryable\u0026lt;TEntity\u0026gt; PageBy\u0026lt;TEntity\u0026gt;(this IQueryable\u0026lt;TEntity\u0026gt; query, PagedRequest pagedRequest) IQueryable\u0026lt;TEntity\u0026gt; SortAndPageBy\u0026lt;TEntity\u0026gt;(this IQueryable\u0026lt;TEntity\u0026gt; query, PagedAndSortedRequest? pagedAndSortedRequest = null); Add a WhereIf extension for conditional queries\nIQueryable\u0026lt;TSource\u0026gt; WhereIf\u0026lt;TSource\u0026gt;(this IQueryable\u0026lt;TSource\u0026gt; source, bool condition, Expression\u0026lt;Func\u0026lt;TSource, bool\u0026gt;\u0026gt; predicate); Using the Extension Methods in an API Controller [HttpGet] [Authorize(IdentityPermissions.Users.Default)] public async Task\u0026lt;ActionResult\u0026lt;PagedResponse\u0026lt;UserListItem\u0026gt;\u0026gt;\u0026gt; GetUsers([FromQuery] UserListRequest model) { IQueryable\u0026lt;User\u0026gt; users = dbContext.Set\u0026lt;User\u0026gt;(); if (model.Keyword is not null) { users = users.Where(e =\u0026gt; e.UserName != null \u0026amp;\u0026amp; e.UserName.Contains(model.Keyword)); } users = users.WhereIf(model.PhoneNumber is not null, e =\u0026gt; e.PhoneNumber == model.PhoneNumber); var pagedUsers = users.SortAndPageBy(model); var list = new List\u0026lt;UserListItem\u0026gt;(); return new PagedResponse\u0026lt;UserListItem\u0026gt;(mapper.Map\u0026lt;List\u0026lt;UserListItem\u0026gt;\u0026gt;(await pagedUsers.ToListAsync()), await users.CountAsync()); } Implementing Flexible Complex Queries You can use OData or GraphQL for more complex queries.\nOData is a REST-based protocol that uses URLs to query and manipulate data. OData filters, sorts, pages, and selects data through URL query string parameters. Example:\nhttp://localhost:8080/api/products?$filter=price gt 100\u0026amp;$orderby=price desc\u0026amp;$top=5\u0026amp;$skip=10 For more information about OData, see the ZeroDegree OData course\nGraphQL is a query language for APIs that offers a more efficient, powerful, and flexible alternative. GraphQL queries and manipulates data through a single endpoint. Example:\nhttp://localhost:8080/graphql query { products(filter: {price: {gt: 100}}, orderBy: {price: desc}, top: 5, skip: 10) { id name price } } ","permalink":"/en/helloshop/paging/","summary":"\u003ch2 id=\"paging-parameters\"\u003ePaging Parameters\u003c/h2\u003e\n\u003cp\u003eRequests should use the GET method\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehttp://localhost:8080/api/products?keyword\u003cspan class=\"o\"\u003e=\u003c/span\u003etest\u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"nv\"\u003epagenumber\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e1\u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"nv\"\u003epagesize\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e5\u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"nv\"\u003eorderby\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003eid desc,price asc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe response is as follows\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;totalCount\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;items\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;test\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;price\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;test\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nt\"\u003e\u0026#34;price\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"paged-request-model\"\u003ePaged Request Model\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePagedAndSortedRequest\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ePagedRequest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring?\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderBy\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"paged-response-model\"\u003ePaged Response Model\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePagedResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003eIReadOnlyList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003etotalCount\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eIReadOnlyList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eT\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eItems\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eitems\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eTotalCount\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etotalCount\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"extending-iqueryable-to-sort-by-property-name\"\u003eExtending IQueryable to Sort by Property Name\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eQueryableOrderByExtensions.cs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eConvert a string condition into a sort expression\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eIOrderedQueryable\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eTSource\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eOrderBy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIQueryable\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eTSource\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003epropertyName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"extending-iqueryable-for-searching-and-sorting\"\u003eExtending IQueryable for Searching and Sorting\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eQueryableExtensions.cs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExtension methods for paging and sorting\u003c/p\u003e","title":"Paging, Sorting, and Multi-Condition Queries"},{"content":"After authentication, identity data is available in HttpContext.User. However, identity is not equal to permission. Permissions should be evaluated by the authorization system.\nAuthorization in ASP.NET Core ASP.NET Core provides a policy-based authorization model. Policies can be defined by role, resource, or custom conditions.\nUse IAuthorizationService IAuthorizationService is the core authorization service in ASP.NET Core:\npublic interface IAuthorizationService { Task\u0026lt;AuthorizationResult\u0026gt; AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable\u0026lt;IAuthorizationRequirement\u0026gt; requirements); Task\u0026lt;AuthorizationResult\u0026gt; AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName); Task\u0026lt;AuthorizationResult\u0026gt; AuthorizeAsync(ClaimsPrincipal user, object resource, AuthorizationPolicy policy); } AuthorizationResult contains success/failure information and reasons.\nImplement a custom policy provider You can use OperationAuthorizationRequirement to model permission requirements and inject them into policies.\npublic class CustomAuthorizationPolicyProvider(IOptions\u0026lt;AuthorizationOptions\u0026gt; options, IPermissionDefinitionManager permissionDefinitionManager) : DefaultAuthorizationPolicyProvider(options) Multiple policies are OR-ed; multiple requirements in one policy are AND-ed.\nModel authorization resources public interface IAuthorizationResource { string ResourceType =\u0026gt; GetType().Name; string ResourceId =\u0026gt; GetType().GetProperty(\u0026#34;Id\u0026#34;)?.GetValue(this)?.ToString() ?? throw new NotImplementedException(); } Define a resource record public record struct ResourceInfo : IAuthorizationResource Implement authorization handlers public class PermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler\u0026lt;OperationAuthorizationRequirement\u0026gt; public class ResourcePermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler\u0026lt;OperationAuthorizationRequirement, IAuthorizationResource\u0026gt; protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement); protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, TResource resource); Use IPermissionChecker in handler public class PermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler\u0026lt;OperationAuthorizationRequirement\u0026gt; { private readonly IPermissionChecker _permissionChecker; public PermissionRequirementHandler(IPermissionChecker permissionChecker) { _permissionChecker = permissionChecker; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement) { if (await _permissionChecker.IsGrantedAsync(context.User, requirement.Name)) { context.Succeed(requirement); } } } ","permalink":"/en/helloshop/authorization/","summary":"\u003cp\u003eAfter authentication, identity data is available in \u003ccode\u003eHttpContext.User\u003c/code\u003e. However, identity is not equal to permission. Permissions should be evaluated by the authorization system.\u003c/p\u003e\n\u003ch2 id=\"authorization-in-aspnet-core\"\u003eAuthorization in ASP.NET Core\u003c/h2\u003e\n\u003cp\u003eASP.NET Core provides a policy-based authorization model. Policies can be defined by role, resource, or custom conditions.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"authorization\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/authorization.svg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"use-iauthorizationservice\"\u003eUse \u003ccode\u003eIAuthorizationService\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eIAuthorizationService\u003c/code\u003e is the core authorization service in ASP.NET Core:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003einterface\u003c/span\u003e \u003cspan class=\"nc\"\u003eIAuthorizationService\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eAuthorizationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eAuthorizeAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eClaimsPrincipal\u003c/span\u003e \u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eobject\u003c/span\u003e \u003cspan class=\"n\"\u003eresource\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eIEnumerable\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIAuthorizationRequirement\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003erequirements\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eAuthorizationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eAuthorizeAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eClaimsPrincipal\u003c/span\u003e \u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eobject\u003c/span\u003e \u003cspan class=\"n\"\u003eresource\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003epolicyName\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eTask\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eAuthorizationResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eAuthorizeAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eClaimsPrincipal\u003c/span\u003e \u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eobject\u003c/span\u003e \u003cspan class=\"n\"\u003eresource\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eAuthorizationPolicy\u003c/span\u003e \u003cspan class=\"n\"\u003epolicy\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eAuthorizationResult\u003c/code\u003e contains success/failure information and reasons.\u003c/p\u003e\n\u003ch2 id=\"implement-a-custom-policy-provider\"\u003eImplement a custom policy provider\u003c/h2\u003e\n\u003cp\u003eYou can use \u003ccode\u003eOperationAuthorizationRequirement\u003c/code\u003e to model permission requirements and inject them into policies.\u003c/p\u003e","title":"Policy-based and Resource-based Authorization"},{"content":"Basic Concepts A single operating system can run multiple PostgreSQL instances, each with its own configuration file, data directory, and port. Each instance can host multiple databases, and each database can contain multiple tables. Docker makes it easy to create multiple PostgreSQL instances.\nInstalling the Primary and Replica Servers docker run --name postgres1 -e POSTGRES_PASSWORD=postgres -d -p 5431:5432 -v ${pwd}/postgres1/data:/var/lib/postgresql/data postgres docker run --name postgres2 -e POSTGRES_PASSWORD=postgres -d -p 5432:5432 -v ${pwd}/postgres2/data:/var/lib/postgresql/data postgres Logical Replication Logical replication is a method of replicating data objects and their changes based on a replication identity (usually the primary key). We use the term \u0026ldquo;logical\u0026rdquo; in contrast to physical replication. Logical replication uses a publish and subscribe model, in which one or more subscribers subscribe to one or more publications on a publisher node. Subscribers pull data from the publications they subscribe to, and may subsequently re-publish the data to allow cascading replication or more complex configurations.\nCreating the Primary and Replica Databases Create the database mydb1 in postgres1.\nCREATE DATABASE mydb1; Create the database mydb2 in postgres2.\nCREATE DATABASE mydb2; Create a table mytable with the same structure in both the mydb1 and mydb2 databases.\nCREATE TABLE mytable (id int PRIMARY KEY, name text NOT NULL); Setting the Server WAL Level to Logical Set the server WAL level to logical in the mydb1 database of postgres1.\nALTER SYSTEM SET wal_level = logical; Set the server WAL level to logical in the mydb2 database of postgres2.\nALTER SYSTEM SET wal_level = logical; Creating a Publication on the Primary Create a publication in the mydb1 primary database of postgres1. Create a publication for all tables.\nCREATE PUBLICATION my_pub FOR ALL TABLES; Create a publication for specific tables.\nCREATE PUBLICATION my_pub FOR TABLE mytable; CREATE PUBLICATION my_pub FOR TABLE mytable1, mytable2; Creating a Subscription on the Replica Create a subscription in the mydb2 replica database of postgres2. CREATE SUBSCRIPTION my_sub CONNECTION \u0026#39;dbname=mydb1 host=postgres1_ip_address port=5431 user=postgres password=postgres\u0026#39; PUBLICATION my_pub Testing Logical Replication Create the table mytable in both the mdb1 database on the postgres1 primary and the mydb2 database on the postgres2 replica.\nInsert data into mydb1 on the postgres1 primary.\nINSERT INTO mytable VALUES (1,\u0026#39;hello\u0026#39;); Query the data in mydb2 on the postgres2 replica.\nSELECT * FROM mytable; Notes For newly created tables, the publication and subscription must be recreated. If the publication was created with FOR ALL TABLES, new tables are added to the publication automatically, but the subscription still needs to be recreated.\nAlternatively, you can use graphical tools such as pgAdmin to create publications and subscriptions.\nStreaming Replication Streaming replication is a physical replication method that replicates data by transferring WAL records from one server to another. Streaming replication is a built-in feature of PostgreSQL and can replicate data across different servers.\nPrimary Server Configuration Create a replication user on postgres1 to avoid the security issues of using a superuser. Create a user named testuser, granting it only login and replication privileges, with the password testpwd.\nCREATE ROLE testuser LOGIN REPLICATION ENCRYPTED PASSWORD \u0026#39;testpwd\u0026#39;; Modify the pg_hba.conf file on postgres1 to allow the testuser user to connect to the primary server.\nhost replication testuser all scram-sha-256 Replica Server Configuration export PGPASSWORD=\u0026#39;testpwd\u0026#39; \u0026amp;\u0026amp; pg_basebackup -h postgres1_ip_address -p 5431 -D /var/lib/postgresql/data/my_data_backup -U testuser -P -X stream -R The command above backs up the data from postgres1 into the /var/lib/postgresql/data/my_data_backup directory. -X stream means streaming replication is used, and -R means the replica server starts automatically once the backup completes. From then on, data from postgres1 is synchronized to postgres2 automatically, with no manual steps required.\nThis command also generates a file named standby.signal in the my_data_backup directory, indicating that the replica server has started, and adds a primary_conninfo setting to the postgresql.auto.conf file, which holds the connection information for the primary server.\nReplace the data directory of postgres2 by moving the backed-up data directory into postgres2\u0026rsquo;s data directory location.\nrm -rf /var/lib/postgresql/data mv /var/lib/postgresql/data/my_data_backup /var/lib/postgresql/data Differences Between Asynchronous and Synchronous Streaming Replication In asynchronous replication, the primary server sends WAL records to the replica but does not wait for the replica\u0026rsquo;s acknowledgment, continuing to process other transactions instead. This improves primary server performance but may lose data.\nIn synchronous replication, the primary server sends WAL records to the replica and waits for its acknowledgment; only after the replica confirms does the primary continue processing other transactions. This guarantees data consistency but reduces primary server performance.\nConfiguring Synchronous Replication on the Primary Run the following command on postgres1.\nALTER SYSTEM SET synchronous_standby_names = \u0026#39;standby2\u0026#39;; Replica Server Configuration Modify the postgresql.auto.conf file in postgres2\u0026rsquo;s data directory and add the following setting.\nprimary_conninfo = \u0026#39;application_name=standby2\u0026#39; https://www.cnblogs.com/haha029/p/16721007.html\n","permalink":"/en/helloshop/postgresql-master-slave/","summary":"\u003ch2 id=\"basic-concepts\"\u003eBasic Concepts\u003c/h2\u003e\n\u003cp\u003eA single operating system can run multiple PostgreSQL instances, each with its own configuration file, data directory, and port. Each instance can host multiple databases, and each database can contain multiple tables. Docker makes it easy to create multiple PostgreSQL instances.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"postgresql-host\" loading=\"lazy\" src=\"https://oss.xcode.me/notes/helloshop/postgresql-host.svg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"installing-the-primary-and-replica-servers\"\u003eInstalling the Primary and Replica Servers\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run --name postgres1 -e \u003cspan class=\"nv\"\u003ePOSTGRES_PASSWORD\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003epostgres -d -p 5431:5432 -v \u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003epwd\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e/postgres1/data:/var/lib/postgresql/data postgres\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run --name postgres2 -e \u003cspan class=\"nv\"\u003ePOSTGRES_PASSWORD\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003epostgres -d -p 5432:5432 -v \u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003epwd\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e/postgres2/data:/var/lib/postgresql/data postgres\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"logical-replication\"\u003eLogical Replication\u003c/h2\u003e\n\u003cp\u003eLogical replication is a method of replicating data objects and their changes based on a replication identity (usually the primary key). We use the term \u0026ldquo;logical\u0026rdquo; in contrast to physical replication. Logical replication uses a publish and subscribe model, in which one or more subscribers subscribe to one or more publications on a publisher node. Subscribers pull data from the publications they subscribe to, and may subsequently re-publish the data to allow cascading replication or more complex configurations.\u003c/p\u003e","title":"PostgreSQL High Availability and Read/Write Splitting"},{"content":"Data seeding is the process of populating a database with a set of initial data. It is usually performed when the database is first created. These data are usually static and will not change over time. Data seeding is usually used to fill in some basic data, such as users, roles, permissions, products, classifications, etc.\nUniversal interface design public interface IDataSeedingProvider { Task SeedingAsync(IServiceProvider serviceProvider); } Implement specific interface public class DataSeedingProvider(UserManager\u0026lt;User\u0026gt; userManager) : IDataSeedingProvider { public async Task SeedingAsync(IServiceProvider ServiceProvider) { var user = await userManager.FindByNameAsync(\u0026#34;admin\u0026#34;); if (user == null) { await userManager.CreateAsync(new User { UserName = \u0026#34;admin\u0026#34;, Email = \u0026#34;admin@test.com\u0026#34; }, \u0026#34;admin\u0026#34;); } } } Register in the container builder.Services.AddTransient\u0026lt;IDataSeedingProvider, DataSeedingProvider\u0026gt;(); Automated execution of extensions public static class DataSeedingExtensions { public static IApplicationBuilder UseDataSeedingProviders(this IApplicationBuilder app) { using var serviceScope = app.ApplicationServices.CreateScope(); var dataSeedingProviders = serviceScope.ServiceProvider.GetServices\u0026lt;IDataSeedingProvider\u0026gt;(); foreach (IDataSeedingProvider dataSeedingProvider in dataSeedingProviders) { dataSeedingProvider.SeedingAsync(serviceScope.ServiceProvider).Wait(); } return app; } } Automatic registration implementation public static class DataSeedingExtensions { public static IServiceCollection AddDataSeedingProviders(this IServiceCollection services, Assembly? assembly = null) { assembly ??= Assembly.GetCallingAssembly(); var dataSeedProviders = assembly.ExportedTypes.Where(t =\u0026gt; t.IsAssignableTo(typeof(IDataSeedingProvider)) \u0026amp;\u0026amp; t.IsClass); dataSeedProviders.ToList().ForEach(t =\u0026gt; services.AddTransient(typeof(IDataSeedingProvider), t)); return services; } } Execution order problem The execution order of automatically registered providers is uncertain. If you need to determine the execution order, you can design the Order attribute, and then order execution according to the Order attribute in the UseDataSeedingProviders method.\nUsed in microservices builder.Services.AddDataSeedingProviders(); app.UseDataSeedingProviders(); ","permalink":"/en/helloshop/data-seeding/","summary":"\u003cp\u003eData seeding is the process of populating a database with a set of initial data. It is usually performed when the database is first created. These data are usually static and will not change over time. Data seeding is usually used to fill in some basic data, such as users, roles, permissions, products, classifications, etc.\u003c/p\u003e\n\u003ch2 id=\"universal-interface-design\"\u003eUniversal interface design\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003einterface\u003c/span\u003e \u003cspan class=\"nc\"\u003eIDataSeedingProvider\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eSeedingAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIServiceProvider\u003c/span\u003e \u003cspan class=\"n\"\u003eserviceProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"implement-specific-interface\"\u003eImplement specific interface\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eDataSeedingProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eUserManager\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003euserManager\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIDataSeedingProvider\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003easync\u003c/span\u003e \u003cspan class=\"n\"\u003eTask\u003c/span\u003e \u003cspan class=\"n\"\u003eSeedingAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIServiceProvider\u003c/span\u003e \u003cspan class=\"n\"\u003eServiceProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003euser\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003euserManager\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFindByNameAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;admin\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003euser\u003c/span\u003e \u003cspan class=\"p\"\u003e==\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003euserManager\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"n\"\u003eUser\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eUserName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;admin\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eEmail\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;admin@test.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;admin\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"register-in-the-container\"\u003eRegister in the container\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddTransient\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIDataSeedingProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eDataSeedingProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"automated-execution-of-extensions\"\u003eAutomated execution of extensions\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eDataSeedingExtensions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"n\"\u003eIApplicationBuilder\u003c/span\u003e \u003cspan class=\"n\"\u003eUseDataSeedingProviders\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003ethis\u003c/span\u003e \u003cspan class=\"n\"\u003eIApplicationBuilder\u003c/span\u003e \u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"nn\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003eserviceScope\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eApplicationServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreateScope\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edataSeedingProviders\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eserviceScope\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServiceProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetServices\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIDataSeedingProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIDataSeedingProvider\u003c/span\u003e \u003cspan class=\"n\"\u003edataSeedingProvider\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edataSeedingProviders\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003edataSeedingProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSeedingAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eserviceScope\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServiceProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eWait\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eapp\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"automatic-registration-implementation\"\u003eAutomatic registration implementation\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eDataSeedingExtensions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"n\"\u003eIServiceCollection\u003c/span\u003e \u003cspan class=\"n\"\u003eAddDataSeedingProviders\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003ethis\u003c/span\u003e \u003cspan class=\"n\"\u003eIServiceCollection\u003c/span\u003e \u003cspan class=\"n\"\u003eservices\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"n\"\u003eassembly\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eassembly\u003c/span\u003e \u003cspan class=\"p\"\u003e??=\u003c/span\u003e \u003cspan class=\"n\"\u003eAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetCallingAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kt\"\u003evar\u003c/span\u003e \u003cspan class=\"n\"\u003edataSeedProviders\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eassembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eExportedTypes\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eWhere\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003et\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsAssignableTo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003etypeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIDataSeedingProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIsClass\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edataSeedProviders\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToList\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eForEach\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003et\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eservices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddTransient\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003etypeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIDataSeedingProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eservices\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"execution-order-problem\"\u003eExecution order problem\u003c/h2\u003e\n\u003cp\u003eThe execution order of automatically registered providers is uncertain. If you need to determine the execution order, you can design the Order attribute, and then order execution according to the Order attribute in the UseDataSeedingProviders method.\u003c/p\u003e","title":"Provider used to generate initial data"},{"content":"rabbitmq-plugins enable rabbitmq_management Default User：guest/guest Add User：test/test http://192.168.0.202:15672\nhttp://mqtt.test:15672\nrabbitmq-plugins enable rabbitmq_mqtt rabbitmq-plugins disable rabbitmq_mqtt rabbitmq-plugins enable rabbitmq_web_mqtt rabbitmq-plugins disable rabbitmq_web_mqtt rabbitmq-plugins enable_feature_flag all rabbitmq-plugins disable_feature_flag all rabbitmq-plugins enable rabbitmq_auth_backend_http Modify the rabbitmq.conf configuration file, located at %AppData%\\RabbitMQ\nauth_backends.1 = http auth_http.http_method = post auth_http.user_path = http://some-server/api/RabbitMqAuth/User auth_http.vhost_path = http://some-server/api/RabbitMqAuth/Vhost auth_http.resource_path = http://some-server/api/RabbitMqAuth/Resource auth_http.topic_path = http://some-server/api/RabbitMqAuth/Topic RabbitMQ server to perform authentication\n","permalink":"/en/mqtt/rabbitmq-management-mqtt/","summary":"\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erabbitmq-plugins enable rabbitmq_management\n\u003c/code\u003e\u003c/pre\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eDefault User：guest/guest\nAdd User：test/test\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ca href=\"http://192.168.0.202:15672\"\u003ehttp://192.168.0.202:15672\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://mqtt.test:15672\"\u003ehttp://mqtt.test:15672\u003c/a\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erabbitmq-plugins enable rabbitmq_mqtt\nrabbitmq-plugins disable rabbitmq_mqtt\n\nrabbitmq-plugins enable rabbitmq_web_mqtt\nrabbitmq-plugins disable rabbitmq_web_mqtt\n\nrabbitmq-plugins enable_feature_flag all\nrabbitmq-plugins disable_feature_flag all\n\u003c/code\u003e\u003c/pre\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erabbitmq-plugins enable rabbitmq_auth_backend_http\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eModify the rabbitmq.conf configuration file, located at %AppData%\\RabbitMQ\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eauth_backends.1 = http\nauth_http.http_method = post\nauth_http.user_path = http://some-server/api/RabbitMqAuth/User\nauth_http.vhost_path = http://some-server/api/RabbitMqAuth/Vhost\nauth_http.resource_path = http://some-server/api/RabbitMqAuth/Resource\nauth_http.topic_path = http://some-server/api/RabbitMqAuth/Topic\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ca href=\"https://github.com/rabbitmq/rabbitmq-server/tree/main/deps/rabbitmq_auth_backend_http\"\u003eRabbitMQ server to perform authentication\u003c/a\u003e\u003c/p\u003e","title":"RabbitMQ Management"},{"content":"Friends, the new generation of the Zero framework officially starts being built today. We are very excited to announce that the new framework is named the HelloShop project. This framework will demonstrate a development architecture built on the new generation of the .NET technology stack. The name HelloShop was chosen because a simple shop application can demonstrate all the technologies of a system. Of course, this shop system could just as well be any other system — as long as you understand the design philosophy of this framework, you can quickly build a system of your own. The following document was generated by AI and may contain some grammatical errors, which we will correct later.\nBusiness Scenario This shop application is intended to demonstrate all the technologies of a system, helping developers build a system with minimal dependencies and code, placing different technologies within a microservice architecture. Developers can quickly reuse code and assemble a system according to their own business needs, instead of spending a lot of time on a general-purpose framework.\nOur goal is to provide a fine-grained microservice architecture and best practices, so that you can combine various technologies yourself: a unified authentication and authorization system, service discovery, load balancing, fault tolerance, distributed tracing, distributed caching, distributed transactions, distributed messaging, distributed logging, distributed configuration, distributed scheduled tasks, and more.\nIn addition, we also provide UIs, including a Blazor-based web app and a MAUI-based hybrid app, which can run on different platforms, including desktop applications, Android apps, and iOS apps.\n{class=\u0026ldquo;img-responsive w-100\u0026rdquo;}\nIncluded Technologies Visual Studio 2022, .NET 8, C# 12.0, Aspire, ASP.NET Core, EF Core, WebApi, gRPC, Blazor, MAUI, PostgreSQL, MongoDB, Redis, SignalR, Identity, Orleans, logging, secret management, background scheduled tasks, service discovery, publish/subscribe, distributed tracing, health checks, performance metric probing, configuration management, containerization, unit testing and integration testing, and Roslyn-based source generators and incremental generators.\nRepository Structure For the repository structure, we follow the style Microsoft commonly uses. The directory structure is as follows:\nassets Static resources, including images, icons, videos, audio, etc.\nbuild Build scripts, including compile scripts, packaging scripts, publishing scripts, etc.\ndocs Related documentation, including design documents, architecture documents, development documents, deployment documents, etc.`\nsamples Demo examples, including code samples, configuration samples, data samples, documentation samples, etc.\nsrc Source code, including source code, configuration files, resource files, script files, etc.\ntests Test code, including unit tests, integration tests, end-to-end tests, etc.\ntools Some tools used by the project.\nCode Repository International repository: https://github.com/bit365/hello-shop{target=\u0026quot;_blank\u0026quot;}\nDomestic (China) repository: https://gitee.com/bit365/hello-shop{target=\u0026quot;_blank\u0026quot;}\nFollow-up Plans We have also just registered the two domains helloshopnet.com and helloshopnet.cn for later deployment of the framework — you could say we are giving HelloShop a home. They are currently going through the ICP filing process.\nTechnical Discussion There will be a series of companion videos for this framework published on www.xcode.me. If you want to get the latest videos, you can follow our WeChat subscription account zerostack, where we will post the latest updates. If you find this framework helpful, you can give us a Star — that is the greatest encouragement for us.\nFinally, we hope this framework helps you. If you have any questions, you can raise them on GitHub and we will reply as soon as possible. Insisting on elegant coding is what we have always pursued, and we hope you can take part in that elegance too.\n","permalink":"/en/helloshop/readme/","summary":"\u003cp\u003eFriends, the new generation of the Zero framework officially starts being built today. We are very excited to announce that the new framework is named the HelloShop project. This framework will demonstrate a development architecture built on the new generation of the .NET technology stack. The name HelloShop was chosen because a simple shop application can demonstrate all the technologies of a system. Of course, this shop system could just as well be any other system — as long as you understand the design philosophy of this framework, you can quickly build a system of your own. The following document was generated by AI and may contain some grammatical errors, which we will correct later.\u003c/p\u003e","title":"Readme"},{"content":"Locks in Single-Process Applications In a single-machine environment, we can use thread locks to solve concurrency problems. In a distributed system, however, thread locks cannot solve concurrency problems, because a thread lock in a distributed system can only lock the current process, not other processes.\nIn .NET, we can use the lock keyword to implement a thread lock.\nhttps://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/lock\nprivate static readonly object _lock = new object(); public void DoSomething() { lock (_lock) { // Business logic } } C# 13 introduces a new thread synchronization type, System.Threading.Lock, which simplifies lock usage through scope management, making the code clearer and more reliable.\nusing System.Threading.Lock; private static readonly Lock _lock = new Lock(); public void DoSomething() { using (_lock.EnterScope()) { // Business logic } } Open-Source Distributed Lock Implementations DistributedLock is a .NET library that provides strong, easy-to-use distributed mutexes, reader-writer locks, and semaphores built on a variety of underlying technologies.\nhttps://github.com/madelson/DistributedLock\nDistributed Locks in the Zero Framework The Zero Framework provides a basic abstraction for distributed locks and implements a distributed lock based on Dapr.\nhttps://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview\nHow the Redis Distributed Lock Works The Redis distributed lock is implemented with the SETNX command. SETNX is an atomic Redis operation that sets a key\u0026rsquo;s value only if the key does not exist; if the key already exists, it does nothing.\n","permalink":"/en/helloshop/distributed-lock/","summary":"\u003ch2 id=\"locks-in-single-process-applications\"\u003eLocks in Single-Process Applications\u003c/h2\u003e\n\u003cp\u003eIn a single-machine environment, we can use thread locks to solve concurrency problems. In a distributed system, however, thread locks cannot solve concurrency problems, because a thread lock in a distributed system can only lock the current process, not other processes.\u003c/p\u003e\n\u003cp\u003eIn .NET, we can use the \u003ccode\u003elock\u003c/code\u003e keyword to implement a thread lock.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/lock\"\u003ehttps://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/lock\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"k\"\u003ereadonly\u003c/span\u003e \u003cspan class=\"kt\"\u003eobject\u003c/span\u003e \u003cspan class=\"n\"\u003e_lock\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"kt\"\u003eobject\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eDoSomething\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003elock\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_lock\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e// Business logic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eC# 13 introduces a new thread synchronization type, System.Threading.Lock, which simplifies lock usage through scope management, making the code clearer and more reliable.\u003c/p\u003e","title":"Solving Concurrency Problems with Distributed Locks"},{"content":"Preparing the Database docker run --name postgres -e POSTGRES_PASSWORD=postgres -e TZ=Asia/Shanghai -d -p 5432:5432 postgres docker run --name pgadmin -e PGADMIN_DEFAULT_EMAIL=test@test.com -e PGADMIN_DEFAULT_PASSWORD=test -e TZ=Asia/Shanghai -d -p 5050:80 dpage/pgadmin4 Initializing the Dapr Runtime dapr init -s --from-dir C:\\dapr Extending the Wait Time for RabbitMQ Startup public static IResourceBuilder\u0026lt;IDaprSidecarResource\u0026gt; WithReference(this IResourceBuilder\u0026lt;IDaprSidecarResource\u0026gt; builder, IResourceBuilder\u0026lt;IResourceWithConnectionString\u0026gt; resourceBuilder, int waitInSeconds = 10) Updating MediatR Dependency Injection Previously, behaviors were registered with the options.AddBehavior method; now they are registered with the options.AddOpenBehavior method.\nbuilder.Services.AddMediatR(options =\u0026gt; { options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); options.AddOpenBehavior(typeof(LoggingBehavior\u0026lt;,\u0026gt;)); options.AddOpenBehavior(typeof(ValidatorBehavior\u0026lt;,\u0026gt;)); options.AddOpenBehavior(typeof(TransactionBehavior\u0026lt;,\u0026gt;)); }); Microservices Using Distributed Events Need the HttpContext Injected builder.Services.AddHttpContextAccessor(); Dropping a Database in Azure Data Studio -- Terminate all sessions connected to the target database SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = \u0026#39;my_database\u0026#39; AND pid \u0026lt;\u0026gt; pg_backend_pid(); -- Drop the database DROP DATABASE my_database; Automatically Creating the Database Before IdentityService Demo Data Generation await serviceProvider.GetRequiredService\u0026lt;IdentityServiceDbContext\u0026gt;().Database.EnsureCreatedAsync(cancellationToken); Consistently Use DateTimeOffset Instead of DateTime The advantage of using UTC time is that it can be converted between time zones without losing time information, which helps programs handle time better and benefits the internationalization and localization of cross-time-zone applications.\nvar utcNow = DateTimeOffset.UtcNow; When displaying time, you can use the ToLocalTime method to convert it to local time.\nvar localTime = utcNow.LocalDateTime; The time type in PostgreSQL databases is timestamp with time zone, which converts time to UTC for storage.\nAll times in a PostgreSQL database are UTC, but they are converted to local time according to the time zone when queried. This process is transparent and automatic.\nRemove the previously configured, inelegant time conversion settings.\nAppContext.SetSwitch(\u0026#34;Npgsql.EnableLegacyTimestampBehavior\u0026#34;, true); AppContext.SetSwitch(\u0026#34;Npgsql.DisableDateTimeInfinityConversions\u0026#34;, true); Prefer TimeProvider for Getting the Current Time Use the TimeProvider abstract class whenever possible, because it makes it easier to mock time in tests.\nvar now = TimeProvider.System.Now; var utcNow = TimeProvider.System.UtcNow; which is equivalent to\nvar now = DateTimeOffset.Now; var utcNow = DateTimeOffset.UtcNow; public class MyTimeProvider: TimeProvider { public override DateTimeOffset Now =\u0026gt; new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero); public override DateTimeOffset UtcNow =\u0026gt; new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero); } services.AddSingleton\u0026lt;TimeProvider, MyTimeProvider\u0026gt;(); public class MyService { private readonly TimeProvider _timeProvider; public MyService(TimeProvider timeProvider) { _timeProvider = timeProvider; } public void DoSomething() { var now = _timeProvider.Now; var utcNow = _timeProvider.UtcNow; } } Configuring OrderingService Authentication const string issuerSigningKey = ServiceDefaults.Constants.IdentityConstantsIssuerSigningKey; builder.Services.AddAuthentication().AddJwtBearer(options =\u0026gt; { options.TokenValidationParameters.ValidateIssuer = false; options.TokenValidationParameters.ValidateAudience = false; options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(issuerSigningKey)); }); Configuring OrderingService Object Mapping public class OrdersMapConfiguration : Profile { public OrdersMapConfiguration() { CreateMap\u0026lt;CreateOrderRequest, CreateOrderCommand\u0026gt;().ForMember(dest =\u0026gt; dest.OrderItems, opt =\u0026gt; opt.MapFrom(src =\u0026gt; src.Items)); CreateMap\u0026lt;BasketItem, CreateOrderCommand.CreateOrderCommandItem\u0026gt;().ForMember(dest =\u0026gt; dest.Units, opt =\u0026gt; opt.MapFrom(src =\u0026gt; src.Quantity)); CreateMap\u0026lt;CreateOrderCommand.CreateOrderCommandItem, OrderItem\u0026gt;(); CreateMap\u0026lt;CreateOrderCommand, Address\u0026gt;(); } } Configuring the Field Lengths of the Product Entity in ProductService public class ProductEntityTypeConfiguration : IEntityTypeConfiguration\u0026lt;Product\u0026gt; { public void Configure(EntityTypeBuilder\u0026lt;Product\u0026gt; builder) { builder.Property(x =\u0026gt; x.Name).HasMaxLength(64); } } ","permalink":"/en/helloshop/aspire-host/","summary":"\u003ch2 id=\"preparing-the-database\"\u003ePreparing the Database\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run --name postgres -e \u003cspan class=\"nv\"\u003ePOSTGRES_PASSWORD\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003epostgres -e \u003cspan class=\"nv\"\u003eTZ\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003eAsia/Shanghai -d -p 5432:5432 postgres\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run --name pgadmin -e \u003cspan class=\"nv\"\u003ePGADMIN_DEFAULT_EMAIL\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003etest@test.com -e \u003cspan class=\"nv\"\u003ePGADMIN_DEFAULT_PASSWORD\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"nb\"\u003etest\u003c/span\u003e -e \u003cspan class=\"nv\"\u003eTZ\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003eAsia/Shanghai -d -p 5050:80 dpage/pgadmin4\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"initializing-the-dapr-runtime\"\u003eInitializing the Dapr Runtime\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edapr init -s --from-dir C:\u003cspan class=\"se\"\u003e\\d\u003c/span\u003eapr\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"extending-the-wait-time-for-rabbitmq-startup\"\u003eExtending the Wait Time for RabbitMQ Startup\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"n\"\u003eIResourceBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIDaprSidecarResource\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eWithReference\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003ethis\u003c/span\u003e \u003cspan class=\"n\"\u003eIResourceBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIDaprSidecarResource\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eIResourceBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIResourceWithConnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eresourceBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ewaitInSeconds\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"m\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"updating-mediatr-dependency-injection\"\u003eUpdating MediatR Dependency Injection\u003c/h2\u003e\n\u003cp\u003ePreviously, behaviors were registered with the options.AddBehavior method; now they are registered with the options.AddOpenBehavior method.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddMediatR\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eRegisterServicesFromAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetExecutingAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddOpenBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003etypeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eLoggingBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;,\u0026gt;));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddOpenBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003etypeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eValidatorBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;,\u0026gt;));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddOpenBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003etypeof\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTransactionBehavior\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;,\u0026gt;));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"microservices-using-distributed-events-need-the-httpcontext-injected\"\u003eMicroservices Using Distributed Events Need the HttpContext Injected\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddHttpContextAccessor\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"dropping-a-database-in-azure-data-studio\"\u003eDropping a Database in Azure Data Studio\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- Terminate all sessions connected to the target database\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eSELECT\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epg_terminate_backend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epg_stat_activity\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eFROM\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epg_stat_activity\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eWHERE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epg_stat_activity\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edatname\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;my_database\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"k\"\u003eAND\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epid\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026gt;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003epg_backend_pid\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- Drop the database\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eDROP\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eDATABASE\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003emy_database\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"automatically-creating-the-database-before-identityservice-demo-data-generation\"\u003eAutomatically Creating the Database Before IdentityService Demo Data Generation\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eserviceProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetRequiredService\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;().\u003c/span\u003e\u003cspan class=\"n\"\u003eDatabase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eEnsureCreatedAsync\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecancellationToken\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"consistently-use-datetimeoffset-instead-of-datetime\"\u003eConsistently Use DateTimeOffset Instead of DateTime\u003c/h2\u003e\n\u003cp\u003eThe advantage of using UTC time is that it can be converted between time zones without losing time information, which helps programs handle time better and benefits the internationalization and localization of cross-time-zone applications.\u003c/p\u003e","title":"Starting Distributed Microservices with Aspire"},{"content":"Starting a PostgreSQL Database in Docker https://www.postgresql.org\ndocker pull postgres docker run --name postgres -e POSTGRES_PASSWORD=postgres -e TZ=Asia/Shanghai -d -p 5432:5432 postgres Connecting to the PostgreSQL Database with PgAdmin https://www.pgadmin.org\nSHOW timezone; Using the PostgreSQL Database with EF Core dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL Defining the Entity Type public class User { public int Id { get; set; } public string UserName { get; set; } public string PasswordHash { get; set; } public DateTimeOffset CreationTime { get; set; } = DateTimeOffset.UtcNow; } Configuring the Entity Type public class UserEntityTypeConfiguration : IEntityTypeConfiguration\u0026lt;User\u0026gt; { public void Configure(EntityTypeBuilder\u0026lt;User\u0026gt; builder) { builder.ToTable(\u0026#34;Users\u0026#34;); builder.HasKey(x =\u0026gt; x.Id); builder.Property(x =\u0026gt; x.UserName).IsRequired().HasMaxLength(50); builder.Property(x =\u0026gt; x.PasswordHash).IsRequired().HasMaxLength(50); builder.Property(x =\u0026gt; x.CreationTime); } } Creating the DbContext public class IdentityServiceDbContext : DbContext { public IdentityServiceDbContext(DbContextOptions\u0026lt;IdentityServiceDbContext\u0026gt; options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); AppContext.SetSwitch(\u0026#34;Npgsql.EnableLegacyTimestampBehavior\u0026#34;, true); AppContext.SetSwitch(\u0026#34;Npgsql.DisableDateTimeInfinityConversions\u0026#34;, true); } } Database Connection String { \u0026#34;ConnectionStrings\u0026#34;: { \u0026#34;IdentityDatabase\u0026#34;: \u0026#34;Host=localhost;Port=5432;Database=IdentityService;Username=postgres;Password=postgres\u0026#34; } } Registering the DbContext builder.Services.AddDbContext\u0026lt;IdentityServiceDbContext\u0026gt;(options =\u0026gt; { options.UseNpgsql(builder.Configuration.GetConnectionString(\u0026#34;IdentityDatabase\u0026#34;)); }); Migrating the Database dotnet tool install --global dotnet-ef dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add InitialCreate --output-dir EntityFrameworks/Migrations dotnet ef database update PostgreSQL Database Naming Conventions https://github.com/efcore/EFCore.NamingConventions\nDropping the Database and Removing Migrations dotnet ef database drop --force dotnet ef migrations remove Migrating with a Script dotnet ef migrations script EF Core Column Migration Ordering By default, when creating a table with migrations, EF Core orders primary key columns first, then properties of the entity type and owned types, and finally properties of the base type. However, you can specify a different column order:\nprotected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity\u0026lt;Blog\u0026gt;() .Property(b =\u0026gt; b.Url) .HasColumnOrder(2); modelBuilder.Entity\u0026lt;Blog\u0026gt;() .Property(b =\u0026gt; b.Rating) .HasColumnOrder(3); } PostgreSQL Driver Automatic Time Conversion Configuration AppContext.SetSwitch(\u0026#34;Npgsql.EnableLegacyTimestampBehavior\u0026#34;, true); AppContext.SetSwitch(\u0026#34;Npgsql.DisableDateTimeInfinityConversions\u0026#34;, true); ","permalink":"/en/helloshop/efcore-postgresql-provider/","summary":"\u003ch2 id=\"starting-a-postgresql-database-in-docker\"\u003eStarting a PostgreSQL Database in Docker\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://www.postgresql.org\"\u003ehttps://www.postgresql.org\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker pull postgres\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run --name postgres -e \u003cspan class=\"nv\"\u003ePOSTGRES_PASSWORD\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003epostgres -e \u003cspan class=\"nv\"\u003eTZ\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003eAsia/Shanghai -d -p 5432:5432 postgres\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"connecting-to-the-postgresql-database-with-pgadmin\"\u003eConnecting to the PostgreSQL Database with PgAdmin\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://www.pgadmin.org\"\u003ehttps://www.pgadmin.org\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSHOW timezone\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"using-the-postgresql-database-with-ef-core\"\u003eUsing the PostgreSQL Database with EF Core\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Npgsql.EntityFrameworkCore.PostgreSQL\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"defining-the-entity-type\"\u003eDefining the Entity Type\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUser\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eId\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eUserName\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003ePasswordHash\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e \u003cspan class=\"n\"\u003eCreationTime\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"k\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eDateTimeOffset\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUtcNow\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"configuring-the-entity-type\"\u003eConfiguring the Entity Type\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUserEntityTypeConfiguration\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eIEntityTypeConfiguration\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eConfigure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEntityTypeBuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eToTable\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Users\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eHasKey\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eId\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUserName\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eIsRequired\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eHasMaxLength\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e50\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ePasswordHash\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"n\"\u003eIsRequired\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003eHasMaxLength\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e50\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eCreationTime\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-the-dbcontext\"\u003eCreating the DbContext\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eIdentityServiceDbContext\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eDbContext\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eDbContextOptions\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"k\"\u003ebase\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eprotected\u003c/span\u003e \u003cspan class=\"kd\"\u003eoverride\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"n\"\u003eOnModelCreating\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eModelBuilder\u003c/span\u003e \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ebase\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eOnModelCreating\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eApplyConfigurationsFromAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetExecutingAssembly\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eAppContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetSwitch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Npgsql.EnableLegacyTimestampBehavior\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eAppContext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSetSwitch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Npgsql.DisableDateTimeInfinityConversions\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"database-connection-string\"\u003eDatabase Connection String\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;ConnectionStrings\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;IdentityDatabase\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Host=localhost;Port=5432;Database=IdentityService;Username=postgres;Password=postgres\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"registering-the-dbcontext\"\u003eRegistering the DbContext\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eServices\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eAddDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eIdentityServiceDbContext\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;(\u003c/span\u003e\u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eUseNpgsql\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebuilder\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConfiguration\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eGetConnectionString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;IdentityDatabase\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"migrating-the-database\"\u003eMigrating the Database\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet tool install --global dotnet-ef\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet add package Microsoft.EntityFrameworkCore.Design\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet ef migrations add InitialCreate --output-dir EntityFrameworks/Migrations\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet ef database update\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"postgresql-database-naming-conventions\"\u003ePostgreSQL Database Naming Conventions\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/efcore/EFCore.NamingConventions\"\u003ehttps://github.com/efcore/EFCore.NamingConventions\u003c/a\u003e\u003c/p\u003e","title":"Storing Data in a PostgreSQL Database"},{"content":"Automated testing is an excellent way to make sure application code does what its author intends. The Zero Framework provides unit tests, integration tests, and load tests. Testing frameworks on the .NET platform include xUnit, NUnit, and MSTest, with xUnit being the most widely used. Whichever testing framework you use, tests can be run from the command line or from an IDE.\nUnit Tests A unit test is a test that exercises an individual software component or method, also known as the \u0026ldquo;unit of work.\u0026rdquo; Unit tests should only test code within the developer\u0026rsquo;s control; they do not test infrastructure concerns. Infrastructure concerns include interactions with databases, file systems, and network resources. Unit tests in the Zero Framework use the xUnit framework.\nProject naming convention: \u0026lt;ProjectName\u0026gt;.UnitTests.\npublic class Calculator { public int Add(int a, int b) { return a + b; } } public class CalculatorTests { [Fact] public void Add_WhenCalled_ReturnsTheSumOfArguments() { // Arrange var calculator = new Calculator(); // Act var result = calculator.Add(1, 2); // Assert Assert.Equal(3, result); } } Unit Testing Best Practices https://learn.microsoft.com/zh-cn/dotnet/core/testing/unit-testing-best-practices\nIntegration Tests Integration tests, also known as functional tests, differ from unit tests in that they exercise the ability of two or more software components to work together. These tests run against a broader slice of the system under test, whereas unit tests focus on individual component functions. Integration tests typically also cover infrastructure concerns, such as interactions with databases, file systems, and network resources.\nProject naming convention: \u0026lt;ProjectName\u0026gt;.FunctionalTests.\npublic class Controller: ControllerBase { private readonly IService _service; public Controller(IService service) { _service = service; } [HttpGet] public async Task\u0026lt;IActionResult\u0026gt; Get(int id) { var result = await _service.Get(id); return Ok(result); } } public class Service: IService { public async Task\u0026lt;Model\u0026gt; Get(int id) { return await _repository.Get(id); } } public class ServiceTest: IClassFixture\u0026lt;WebApplicationFactory\u0026lt;Startup\u0026gt;\u0026gt; { private readonly WebApplicationFactory\u0026lt;Startup\u0026gt; _factory; public ServiceTest(WebApplicationFactory\u0026lt;Startup\u0026gt; factory) { _factory = factory; } [Fact] public async Task Get_WhenCalled_ReturnsModel() { // Arrange var client = _factory.CreateClient(); var response = await client.GetAsync(\u0026#34;/api/controller/1\u0026#34;); response.EnsureSuccessStatusCode(); var model = await response.Content.ReadAsAsync\u0026lt;Model\u0026gt;(); // Assert Assert.NotNull(model); } } ","permalink":"/en/helloshop/dot-net-core-testing/","summary":"\u003cp\u003eAutomated testing is an excellent way to make sure application code does what its author intends. The Zero Framework provides unit tests, integration tests, and load tests. Testing frameworks on the .NET platform include \u003ccode\u003exUnit\u003c/code\u003e, \u003ccode\u003eNUnit\u003c/code\u003e, and \u003ccode\u003eMSTest\u003c/code\u003e, with \u003ccode\u003exUnit\u003c/code\u003e being the most widely used. Whichever testing framework you use, tests can be run from the command line or from an IDE.\u003c/p\u003e\n\u003ch2 id=\"unit-tests\"\u003eUnit Tests\u003c/h2\u003e\n\u003cp\u003eA unit test is a test that exercises an individual software component or method, also known as the \u0026ldquo;unit of work.\u0026rdquo; Unit tests should only test code within the developer\u0026rsquo;s control; they do not test infrastructure concerns. Infrastructure concerns include interactions with databases, file systems, and network resources. Unit tests in the Zero Framework use the \u003ccode\u003exUnit\u003c/code\u003e framework.\u003c/p\u003e","title":"Testing in the Zero Framework"},{"content":"Update Visual Studio to the Latest Preview Download and install the Visual Studio preview, and install the .NET 9.0 SDK.\nUpdate the Project File Open the project file and change \u0026lt;TargetFramework\u0026gt;net8.0\u0026lt;/TargetFramework\u0026gt; to \u0026lt;TargetFramework\u0026gt;net9.0\u0026lt;/TargetFramework\u0026gt;.\nUpdate NuGet Packages Open the NuGet package manager and update all NuGet packages to the latest version.\nUpdate the Aspire and MAUI Workloads Open the Aspire workload and update all Aspire packages to the latest version.\ndotnet workload install aspire android ios maccatalyst maui-android maui-desktop maui-mobile maui-windows To Add Tab Autocompletion to PowerShell for the .NET CLI https://learn.microsoft.com/zh-cn/dotnet/core/tools/enable-tab-autocomplete\n","permalink":"/en/helloshop/upgrading-from-net-9/","summary":"\u003ch2 id=\"update-visual-studio-to-the-latest-preview\"\u003eUpdate Visual Studio to the Latest Preview\u003c/h2\u003e\n\u003cp\u003eDownload and install the Visual Studio preview, and install the .NET 9.0 SDK.\u003c/p\u003e\n\u003ch2 id=\"update-the-project-file\"\u003eUpdate the Project File\u003c/h2\u003e\n\u003cp\u003eOpen the project file and change \u003ccode\u003e\u0026lt;TargetFramework\u0026gt;net8.0\u0026lt;/TargetFramework\u0026gt;\u003c/code\u003e to \u003ccode\u003e\u0026lt;TargetFramework\u0026gt;net9.0\u0026lt;/TargetFramework\u0026gt;\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"update-nuget-packages\"\u003eUpdate NuGet Packages\u003c/h2\u003e\n\u003cp\u003eOpen the NuGet package manager and update all NuGet packages to the latest version.\u003c/p\u003e\n\u003ch2 id=\"update-the-aspire-and-maui-workloads\"\u003eUpdate the Aspire and MAUI Workloads\u003c/h2\u003e\n\u003cp\u003eOpen the Aspire workload and update all Aspire packages to the latest version.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet workload install aspire android ios maccatalyst maui-android maui-desktop maui-mobile maui-windows\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"to-add-tab-autocompletion-to-powershell-for-the-net-cli\"\u003eTo Add Tab Autocompletion to PowerShell for the .NET CLI\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://learn.microsoft.com/zh-cn/dotnet/core/tools/enable-tab-autocomplete\"\u003ehttps://learn.microsoft.com/zh-cn/dotnet/core/tools/enable-tab-autocomplete\u003c/a\u003e\u003c/p\u003e","title":"Updating from .NET 8.0 to .NET 9.0"},{"content":"What is .NET Upgrade Assistant? .NET Upgrade Assistant is a tool that helps migrate legacy projects to newer .NET versions. It can help you:\nIdentify dependencies and API usage. Recommend an appropriate .NET target framework. Apply or suggest code changes required for migration. Use .NET Upgrade Assistant in Visual Studio You can install the .NET Upgrade Assistant extension in Visual Studio.\nUse .NET Upgrade Assistant from the command line Install the CLI tool:\ndotnet tool install -g upgrade-assistant Update it to the latest version:\ndotnet tool update -g upgrade-assistant Run it in your project folder:\nupgrade-assistant --apply-updates This upgrades the project to newer .NET versions.\n","permalink":"/en/dotnet/upgrade-assistant/","summary":"\u003ch2 id=\"what-is-net-upgrade-assistant\"\u003eWhat is .NET Upgrade Assistant?\u003c/h2\u003e\n\u003cp\u003e.NET Upgrade Assistant is a tool that helps migrate legacy projects to newer .NET versions. It can help you:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIdentify dependencies and API usage.\u003c/li\u003e\n\u003cli\u003eRecommend an appropriate .NET target framework.\u003c/li\u003e\n\u003cli\u003eApply or suggest code changes required for migration.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"use-net-upgrade-assistant-in-visual-studio\"\u003eUse .NET Upgrade Assistant in Visual Studio\u003c/h2\u003e\n\u003cp\u003eYou can install the .NET Upgrade Assistant extension in Visual Studio.\u003c/p\u003e\n\u003ch2 id=\"use-net-upgrade-assistant-from-the-command-line\"\u003eUse .NET Upgrade Assistant from the command line\u003c/h2\u003e\n\u003cp\u003eInstall the CLI tool:\u003c/p\u003e","title":"Upgrade Projects with .NET Upgrade Assistant"},{"content":"Simplified Solution File Format From sln to slnx: the new slnx format is easier to read and maintain, removes redundant information, and makes the project structure clearer.\ndotnet sln migrate Also update the sln files referenced in slnf files to slnx files.\nUpgrading the Target Framework to .NET 10 In the project files, change the target framework from net9.0 to net10.0:\n\u0026lt;TargetFramework\u0026gt;net10.0\u0026lt;/TargetFramework\u0026gt; Upgrading Aspire to 13.0.0 In the project files, update the Aspire package version to 13.0.0\n- \u0026lt;Project Sdk=\u0026#34;Microsoft.NET.Sdk\u0026#34;\u0026gt; + \u0026lt;Project Sdk=\u0026#34;Aspire.AppHost.Sdk/13.0.0\u0026#34;\u0026gt; - \u0026lt;Sdk Name=\u0026#34;Aspire.AppHost.Sdk\u0026#34; Version=\u0026#34;9.5.2\u0026#34; /\u0026gt; \u0026lt;PropertyGroup\u0026gt; \u0026lt;OutputType\u0026gt;Exe\u0026lt;/OutputType\u0026gt; - \u0026lt;TargetFramework\u0026gt;net9.0\u0026lt;/TargetFramework\u0026gt; + \u0026lt;TargetFramework\u0026gt;net10.0\u0026lt;/TargetFramework\u0026gt; \u0026lt;ImplicitUsings\u0026gt;enable\u0026lt;/ImplicitUsings\u0026gt; \u0026lt;Nullable\u0026gt;enable\u0026lt;/Nullable\u0026gt; - \u0026lt;IsAspireHost\u0026gt;true\u0026lt;/IsAspireHost\u0026gt; \u0026lt;UserSecretsId\u0026gt;0afc20a6-cd99-4bf7-aae1-1359b0d45189\u0026lt;/UserSecretsId\u0026gt; \u0026lt;/PropertyGroup\u0026gt; \u0026lt;ItemGroup\u0026gt; - \u0026lt;PackageReference Include=\u0026#34;Aspire.Hosting.AppHost\u0026#34; Version=\u0026#34;9.5.2\u0026#34; /\u0026gt; + \u0026lt;!-- Aspire.Hosting.AppHost is now included automatically by the SDK --\u0026gt; \u0026lt;/ItemGroup\u0026gt; \u0026lt;/Project\u0026gt; Updating Central Package Management Packages in Directory.Packages.props to the Latest List all packages that need updating\ndotnet list package --outdated Upgrading Swagger to the Microsoft.AspNetCore.OpenApi Version The previous approach was to use Swashbuckle.AspNetCore to generate OpenAPI documents in JSON format, and then use Swagger UI to provide an interactive API documentation interface.\nThe Swashbuckle.AspNetCore package includes both the ability to generate OpenAPI documents and to serve Swagger UI.\nNow Microsoft officially provides the Microsoft.AspNetCore.OpenApi package to generate OpenAPI documents, tightly integrated with the ASP.NET Core framework. The UI can be provided by other tools, such as Redoc or a custom front end, or you can continue to use Swagger UI as the front-end presentation layer.\nTest Visibility Remove the following code snippet:\npublic partial class Program { } Integrating MAUI into the Aspire Project Add the following to the project file to enable MAUI support:\nvar mauiapp = builder.AddMauiProject(\u0026#34;hybridapp\u0026#34;, \u0026#34;../HelloWorld.HybridApp/HelloWorld.HybridApp.csproj\u0026#34;); mauiapp.AddWindowsDevice().WithReference(apiService).WaitFor(apiService); mauiapp.AddMacCatalystDevice().WithReference(apiService).WaitFor(apiService); mauiapp.AddiOSSimulator().WithReference(apiService).WaitFor(apiService); mauiapp.AddAndroidEmulator().WithReference(apiService).WaitFor(apiService); Fixing Some Breaking Changes Remove the Aspire.Hosting.AppHost package, since it is now included automatically by the SDK. Check and update any code that uses deprecated APIs, referring to the Aspire 13.0 release notes for guidance. Test the application to make sure everything works, and resolve any issues caused by the upgrade. ","permalink":"/en/helloshop/dotnet-aspire-13-0/","summary":"\u003ch2 id=\"simplified-solution-file-format\"\u003eSimplified Solution File Format\u003c/h2\u003e\n\u003cp\u003eFrom sln to slnx: the new slnx format is easier to read and maintain, removes redundant information, and makes the project structure clearer.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet sln migrate\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlso update the sln files referenced in slnf files to slnx files.\u003c/p\u003e\n\u003ch2 id=\"upgrading-the-target-framework-to-net-10\"\u003eUpgrading the Target Framework to .NET 10\u003c/h2\u003e\n\u003cp\u003eIn the project files, change the target framework from net9.0 to net10.0:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;TargetFramework\u0026gt;\u003c/span\u003enet10.0\u003cspan class=\"nt\"\u003e\u0026lt;/TargetFramework\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"upgrading-aspire-to-1300\"\u003eUpgrading Aspire to 13.0.0\u003c/h2\u003e\n\u003cp\u003eIn the project files, update the Aspire package version to 13.0.0\u003c/p\u003e","title":"Upgrading the Zero Framework to Aspire 13.0"},{"content":"Upgrading the Visual Studio Development Tools Use Visual Studio Installer to upgrade. After upgrading to v17.12, the .NET 9.0 SDK and the Aspire 9.0 workload are installed automatically.\nNon-Visual Studio Development Environments Install .NET Core SDK 9.0 Install the latest .NET SDK 9.0 release\nCheck the .NET SDK version:\ndotnet --version dotnet --list-sdks Upgrade the Aspire Workload Run the following commands in the project directory:\ndotnet workload uninstall aspire dotnet workload install aspire dotnet workload list dotnet workload update Upgrading the Project Files Add the new node to the Host project:\n\u0026lt;Sdk Name=\u0026#34;Aspire.AppHost.Sdk\u0026#34; Version=\u0026#34;9.0.0\u0026#34; /\u0026gt; Change the target framework of all project files to net9.0:\n\u0026lt;TargetFramework\u0026gt;net9.0\u0026lt;/TargetFramework\u0026gt; Install the EF Core base package\ndotnet add package Microsoft.EntityFrameworkCore Upgrade all NuGet packages in the solution\ndotnet package upgrade --to-latest Simplification Tools Available https://learn.microsoft.com/zh-cn/dotnet/core/porting/upgrade-assistant-install\nService Discovery Now Supports the https+http Scheme httpClient.BaseAddress = new Uri(\u0026#34;https+http://identityservice\u0026#34;) ","permalink":"/en/helloshop/upgrade-to-aspire-9/","summary":"\u003ch2 id=\"upgrading-the-visual-studio-development-tools\"\u003eUpgrading the Visual Studio Development Tools\u003c/h2\u003e\n\u003cp\u003eUse Visual Studio Installer to upgrade. After upgrading to v17.12, the .NET 9.0 SDK and the Aspire 9.0 workload are installed automatically.\u003c/p\u003e\n\u003ch2 id=\"non-visual-studio-development-environments\"\u003eNon-Visual Studio Development Environments\u003c/h2\u003e\n\u003ch3 id=\"install-net-core-sdk-90\"\u003eInstall .NET Core SDK 9.0\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"https://dotnet.microsoft.com/zh-cn/download\"\u003eInstall the latest .NET SDK 9.0 release\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eCheck the .NET SDK version:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet --version\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet --list-sdks\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"upgrade-the-aspire-workload\"\u003eUpgrade the Aspire Workload\u003c/h3\u003e\n\u003cp\u003eRun the following commands in the project directory:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet workload uninstall aspire\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet workload install aspire\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet workload list\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet workload update\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"upgrading-the-project-files\"\u003eUpgrading the Project Files\u003c/h2\u003e\n\u003cp\u003eAdd the new node to the Host project:\u003c/p\u003e","title":"Upgrading the Zero Framework to Aspire 9.0"},{"content":"User Experience User Experience (UX) refers to how users feel when using a product. It is a comprehensive concept that includes users\u0026rsquo; perceptions, emotions, attitudes, and moods toward the product. User Experience Design (UXD) is a user-centered design methodology aimed at increasing user satisfaction and improving users\u0026rsquo; understanding and use of the product.\nFactors such as color, typography, layout, and interaction all affect user experience. Therefore, user experience must be considered when designing user interfaces, providing a clean, intuitive, and easy-to-use interface to improve user satisfaction.\nFlat Design and Skeuomorphism Flat Design is a clean, intuitive design style that emphasizes simple and clear interface design, removing cumbersome decorations and redundant elements so that users can understand and operate the interface more easily. Skeuomorphism is a design style that imitates real-world objects, emphasizing realism and detail, which also makes the interface easier for users to understand and operate.\nSearch for \u0026ldquo;Flat vs Skeuomorphism\u0026rdquo; to learn more.\nCommon Design Languages Google Material Design, Microsoft Fluent Design System, Alibaba Ant Design \u0026hellip;\nMaterial Design Design Language https://material.io/design\nComponent Library https://github.com/MudBlazor/MudBlazor\nhttps://github.com/MudBlazor/\nFluent Design System Design Language https://www.microsoft.com/design/fluent\nComponent Library https://github.com/microsoft/fast-blazor\nAnt Design Design Language https://ant.design\nComponent Library https://github.com/ant-design-blazor/ant-design-blazor\nCommercial Component Libraries https://github.com/radzenhq/radzen-blazor\nPrototyping Tools MasterGo and Figma are two commonly used prototyping tools for designing and building user interface prototypes. They offer rich component libraries and interaction effects that help designers quickly create UI prototypes.\nDesign Tokens Design Tokens are a way of defining a design system, covering design elements such as colors, typography, spacing, and shadows. They help designers and developers quickly create a consistent design style and improve design efficiency.\nChoosing Components Factors such as stylistic consistency, ease of use, customizability, performance optimization, documentation quality, community activity, maintenance and updates, and commercial licensing all influence the choice of a component library.\nComplementary Colors Complementary colors are two colors located opposite each other on the color wheel. When two complementary colors are placed together, they produce a strong contrast that makes both colors stand out. Complementary color combinations usually create visual impact, so they should be used carefully in design. Use browser developer tools to monitor contrast to ensure a good user experience.\nCase Study The Aspire dashboard design is based on Fluent Design and adopts a flat design style that is clean, intuitive, and easy to use, improving the user experience.\n","permalink":"/en/helloshop/design/","summary":"\u003ch2 id=\"user-experience\"\u003eUser Experience\u003c/h2\u003e\n\u003cp\u003eUser Experience (UX) refers to how users feel when using a product. It is a comprehensive concept that includes users\u0026rsquo; perceptions, emotions, attitudes, and moods toward the product. User Experience Design (UXD) is a user-centered design methodology aimed at increasing user satisfaction and improving users\u0026rsquo; understanding and use of the product.\u003c/p\u003e\n\u003cp\u003eFactors such as color, typography, layout, and interaction all affect user experience. Therefore, user experience must be considered when designing user interfaces, providing a clean, intuitive, and easy-to-use interface to improve user satisfaction.\u003c/p\u003e","title":"User Interfaces and Design Languages"},{"content":"Comment Lines starting with # or // are comments and are ignored when Visual Studio sends an HTTP request.\nvariable Lines starting with @ define variables using the syntax @VariableName=Value.\n@hostname=localhost @port=44320 GET https://{{hostname}}:{{port}}/weatherforecast multiple requests The request format is:\n[\u0026lt;HTTP-verb\u0026gt;] \u0026lt;url\u0026gt; [\u0026lt;HTTP-version\u0026gt;] [\u0026lt;request-headers\u0026gt;] [\u0026lt;request-body\u0026gt;] GET https://localhost:7220/weatherforecast ### GET https://localhost:7220/weatherforecast?date=2023-05-11\u0026amp;location=98006 ### GET https://localhost:7220/weatherforecast HTTP/3 ### Request headers GET https://localhost:7220/weatherforecast Date: Wed, 27 Apr 2023 07:28:00 GMT ### GET https://localhost:7220/weatherforecast Cache-Control: max-age=604800 Age: 100 ### request body POST https://localhost:7220/weatherforecast Content-Type: application/json { \u0026#34;date\u0026#34;: \u0026#34;2023-05-11\u0026#34;, \u0026#34;location\u0026#34;: \u0026#34;98006\u0026#34; } Create an HTTP file using a template Right-click the ASP.NET Core project Add \u0026gt; New Item ASP.NET Core\u0026gt;General Select HTTP File, then select Add Use the Endpoint Explorer View \u0026gt; Other Windows \u0026gt; Endpoint Resource Management\n","permalink":"/en/visualstudio/use-http-files/","summary":"\u003ch2 id=\"comment\"\u003eComment\u003c/h2\u003e\n\u003cp\u003eLines starting with # or // are comments and are ignored when Visual Studio sends an HTTP request.\u003c/p\u003e\n\u003ch2 id=\"variable\"\u003evariable\u003c/h2\u003e\n\u003cp\u003eLines starting with @ define variables using the syntax @VariableName=Value.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-http\" data-lang=\"http\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e@hostname=localhost\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e@port=44320\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eGET https://{{hostname}}:{{port}}/weatherforecast\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"multiple-requests\"\u003emultiple requests\u003c/h2\u003e\n\u003cp\u003eThe request format is:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-http\" data-lang=\"http\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e[\u0026lt;HTTP-verb\u0026gt;] \u0026lt;url\u0026gt; [\u0026lt;HTTP-version\u0026gt;]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e[\u0026lt;request-headers\u0026gt;]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e[\u0026lt;request-body\u0026gt;]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-http\" data-lang=\"http\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eGET https://localhost:7220/weatherforecast\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e###\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eGET https://localhost:7220/weatherforecast?date=2023-05-11\u0026amp;location=98006\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e###\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003eGET\u003c/span\u003e \u003cspan class=\"nn\"\u003ehttps://localhost:7220/weatherforecast\u003c/span\u003e \u003cspan class=\"kr\"\u003eHTTP\u003c/span\u003e\u003cspan class=\"o\"\u003e/\u003c/span\u003e\u003cspan class=\"m\"\u003e3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"g\"\u003e###\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"request-headers\"\u003eRequest headers\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-http\" data-lang=\"http\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eGET https://localhost:7220/weatherforecast\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eDate: Wed, 27 Apr 2023 07:28:00 GMT\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e###\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eGET https://localhost:7220/weatherforecast\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eCache-Control: max-age=604800\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eAge: 100\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e###\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"request-body\"\u003erequest body\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-http\" data-lang=\"http\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003ePOST https://localhost:7220/weatherforecast\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003eContent-Type: application/json\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e  \u0026#34;date\u0026#34;: \u0026#34;2023-05-11\u0026#34;,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e  \u0026#34;location\u0026#34;: \u0026#34;98006\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"err\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"create-an-http-file-using-a-template\"\u003eCreate an HTTP file using a template\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eRight-click the ASP.NET Core project\u003c/li\u003e\n\u003cli\u003eAdd \u0026gt; New Item\u003c/li\u003e\n\u003cli\u003eASP.NET Core\u0026gt;General\u003c/li\u003e\n\u003cli\u003eSelect HTTP File, then select Add\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"use-the-endpoint-explorer\"\u003eUse the Endpoint Explorer\u003c/h2\u003e\n\u003cp\u003eView \u0026gt; Other Windows \u0026gt; Endpoint Resource Management\u003c/p\u003e","title":"Using .HTTP files in Visual Studio 2022"},{"content":"Hybrid cache can use both memory cache and distributed cache, which can improve cache hit rate and reduce access to distributed cache, thereby improving performance.\nWhen getting data from the cache, it will first get the data from the memory cache, if there is no data in the memory cache, it will get the data from the distributed cache. When writing data to the cache, it writes data to both the in-memory cache and the distributed cache. Service 1 ----\u0026gt; HybridCache ----\u0026gt; MemoryCache ----\u0026gt; DistributedCache https://learn.microsoft.com/zh-cn/azure/architecture/best-practices/caching\nInstallation package dotnet add package Microsoft.Extensions.Caching.Hybrid Configuration service builder.Services.AddHybridCache(); Use the service public class SomeService(HybridCache cache) { private HybridCache _cache = cache; public async Task\u0026lt;string\u0026gt; GetSomeInfoAsync(string name, int id, CancellationToken token = default) { return await _cache.GetOrCreateAsync( $\u0026#34;{name}-{id}\u0026#34;,async cancel =\u0026gt; await GetDataFromTheSourceAsync(name, id, cancel),cancellationToken: token ); } public async Task\u0026lt;string\u0026gt; GetDataFromTheSourceAsync(string name, int id, CancellationToken token) { string someInfo = $\u0026#34;someinfo-{name}-{id}\u0026#34;; return someInfo; } } Remove cache entries by tag public class SomeService(HybridCache cache) { private HybridCache _cache = cache; public async Task\u0026lt;string\u0026gt; GetSomeInfoAsync(string name, int id, CancellationToken token = default) { var tags = new List\u0026lt;string\u0026gt; { \u0026#34;tag1\u0026#34;, \u0026#34;tag2\u0026#34;, \u0026#34;tag3\u0026#34; }; var entryOptions = new HybridCacheEntryOptions { Expiration = TimeSpan.FromMinutes(1), LocalCacheExpiration = TimeSpan.FromMinutes(1) }; return await _cache.GetOrCreateAsync( $\u0026#34;{name}-{id}\u0026#34;, // Unique key to the cache entry async cancel =\u0026gt; await GetDataFromTheSourceAsync(name, id, cancel), entryOptions, tags, cancellationToken: token ); } public async Task\u0026lt;string\u0026gt; GetDataFromTheSourceAsync(string name, int id, CancellationToken token) { string someInfo = $\u0026#34;someinfo-{name}-{id}\u0026#34;; return someInfo; } public async Task RemoveByTagAsync(string tag) { await _cache.RemoveByTagAsync(tag); } } ","permalink":"/en/helloshop/hybrid-cache-library/","summary":"\u003cp\u003eHybrid cache can use both memory cache and distributed cache, which can improve cache hit rate and reduce access to distributed cache, thereby improving performance.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhen getting data from the cache, it will first get the data from the memory cache, if there is no data in the memory cache, it will get the data from the distributed cache.\u003c/li\u003e\n\u003cli\u003eWhen writing data to the cache, it writes data to both the in-memory cache and the distributed cache.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eService 1 ----\u0026gt;  HybridCache  ----\u0026gt;  MemoryCache  ----\u0026gt;  DistributedCache\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://learn.microsoft.com/zh-cn/azure/architecture/best-practices/caching\"\u003ehttps://learn.microsoft.com/zh-cn/azure/architecture/best-practices/caching\u003c/a\u003e\u003c/p\u003e","title":"Using HybridCache hybrid caching library"},{"content":"Primary constructors One major feature in C# 12 is primary constructors. A primary constructor is declared on the type declaration instead of inside the class body. Its parameters can be used directly in members.\nRecord types public record Address(string FirstName, string LastName); Class types public class Person(string firstName, string lastName) { public override string ToString() { return $\u0026#34;{firstName},{lastName}\u0026#34;; } } public Person(string firstName) : this(firstName, \u0026#34;hello\u0026#34;) { } Collection expressions C# 12 adds collection expressions for concise collection initialization.\n// Create an array: int[] a = [1, 2, 3, 4, 5, 6, 7, 8]; // Create a list: List\u0026lt;string\u0026gt; b = [\u0026#34;one\u0026#34;, \u0026#34;two\u0026#34;, \u0026#34;three\u0026#34;]; // Create a span Span\u0026lt;char\u0026gt; c = [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;, \u0026#39;d\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;f\u0026#39;, \u0026#39;h\u0026#39;, \u0026#39;i\u0026#39;]; // Create a jagged 2D array: int[][] twoD = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; // Create a jagged 2D array from variables: int[] row0 = [1, 2, 3]; int[] row1 = [4, 5, 6]; int[] row2 = [7, 8, 9]; int[][] twoDFromVariables = [row0, row1, row2]; Range and index usage sequence[0] is the first element. ^n means counting from the end. For any number n, index ^n is equivalent to sequence.Length - n.\nstring[] strs = [\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;, \u0026#34;c\u0026#34;, \u0026#34;d\u0026#34;, \u0026#34;e\u0026#34;,\u0026#34;f\u0026#34;]; string[] strs1= strs[0..3]; string[] strs2 = strs[2..^2]; string[] strs3 = strs[^3..^1]; Default lambda parameters var IncrementBy = (int source, int increment = 1) =\u0026gt; source + increment; Spread operator int[] row0 = [1, 2, 3]; int[] row1 = [4, 5, 6]; int[] row2 = [7, 8, 9]; int[] single = [.. row0, .. row1, .. row2]; Type aliases using MyPerson = ConsoleApp1.Person; using MyType = (string FirstName,int LastName); ","permalink":"/en/csharp/csharp-12/","summary":"\u003ch2 id=\"primary-constructors\"\u003ePrimary constructors\u003c/h2\u003e\n\u003cp\u003eOne major feature in C# 12 is primary constructors. A primary constructor is declared on the type declaration instead of inside the class body. Its parameters can be used directly in members.\u003c/p\u003e\n\u003ch3 id=\"record-types\"\u003eRecord types\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003erecord\u003c/span\u003e \u003cspan class=\"nc\"\u003eAddress\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eFirstName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eLastName\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"class-types\"\u003eClass types\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePerson\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003efirstName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003elastName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003eoverride\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003eToString\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e$\u0026#34;{firstName},{lastName}\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"n\"\u003ePerson\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"n\"\u003efirstName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"k\"\u003ethis\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efirstName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;hello\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"collection-expressions\"\u003eCollection expressions\u003c/h2\u003e\n\u003cp\u003eC# 12 adds collection expressions for concise collection initialization.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Create an array:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e6\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e7\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Create a list:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eList\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eb\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;one\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;two\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;three\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Create a span\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eSpan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003ec\u003c/span\u003e  \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"sc\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;b\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;c\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;d\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;e\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;f\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;h\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"sc\"\u003e\u0026#39;i\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Create a jagged 2D array:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[][]\u003c/span\u003e \u003cspan class=\"n\"\u003etwoD\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e6\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e7\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e9\u003c/span\u003e\u003cspan class=\"p\"\u003e]];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Create a jagged 2D array from variables:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003erow0\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003erow1\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e6\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003erow2\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"m\"\u003e7\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e9\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e[][]\u003c/span\u003e \u003cspan class=\"n\"\u003etwoDFromVariables\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003erow0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erow1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erow2\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"range-and-index-usage\"\u003eRange and index usage\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003esequence[0]\u003c/code\u003e is the first element. \u003ccode\u003e^n\u003c/code\u003e means counting from the end. For any number \u003ccode\u003en\u003c/code\u003e, index \u003ccode\u003e^n\u003c/code\u003e is equivalent to \u003ccode\u003esequence.Length - n\u003c/code\u003e.\u003c/p\u003e","title":"What is New in C# 12"},{"content":"Remove the IsAspireHost property from the Host project \u0026lt;IsAspireHost\u0026gt;true\u0026lt;/IsAspireHost\u0026gt; Workload upgrade dotnet workload update Upgrade Visual Studio to the latest version Upgrade to the latest version via Visual Studio Installer.\nItems can be upgraded using AI GitHub Copilot App Modernization - Upgrade for .NET is a powerful Visual Studio extension that works with you to upgrade projects to newer versions of .NET, upgrade dependencies, and apply code fixes.\nGitHub Copilot application modernization is distributed as a Visual Studio extension and is an interactive upgrade process.\nGitHub Copilot app modernization - upgrade for .NET\nUpdate project template dotnet new update Use the upgrade assistant to upgrade your project to the latest dotnet tool install -g UpgradeAssistant dotnet upgrade-assistant upgrade You can also use Visual Studio\u0026rsquo;s Upgrade Assistant.\nAbout the upgrade of CPM central package management The upgrade to CPM Central Package Management is an important update that allows you to use central package version management in your projects. Upgrading CPM currently requires a manual update.\nDashboard upgrade GitHub Copilot enhances the OpenTelemetry debugging and diagnostics experience for dashboards.\nThe Resource Graph view supports context menus.\nOn the Traces page, the dashboard can now visualize outgoing calls to resources that do not emit their own telemetry data\nMost containers support chained configuration var pgPwd = builder.AddParameter(\u0026#34;pg-pwd\u0026#34;, secret: true); builder.AddPostgres(\u0026#34;pg\u0026#34;).WithHostPort(6045) Support custom URL links Define custom resource URLs\nGateway YARP started as Aspire resource var yarp = builder.AddYarp(\u0026#34;myyarp\u0026#34;).WithConfigFile(\u0026#34;path/to/config/file\u0026#34;); Support CLI tools Aspire CLI tools can be installed via NuGet packages.\ndotnet tool install --global aspire.cli --prerelease aspire new aspire run aspire add redis aspire publish --publisher docker-compose Support Publishers deployment Publishers are a new extension point in .NET.NET Aspire that allow you to define how distributed applications are converted into deployable assets.\nThe Aspire.Hosting.Docker and Aspire.Hosting.Kubernetes extensions provide Docker and Kubernetes publishers.\n","permalink":"/en/helloshop/dotnet-aspire-9-3/","summary":"\u003ch2 id=\"remove-the-isaspirehost-property-from-the-host-project\"\u003eRemove the IsAspireHost property from the Host project\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;IsAspireHost\u0026gt;\u003c/span\u003etrue\u003cspan class=\"nt\"\u003e\u0026lt;/IsAspireHost\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"workload-upgrade\"\u003eWorkload upgrade\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edotnet workload update\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"upgrade-visual-studio-to-the-latest-version\"\u003eUpgrade Visual Studio to the latest version\u003c/h2\u003e\n\u003cp\u003eUpgrade to the latest version via Visual Studio Installer.\u003c/p\u003e\n\u003ch2 id=\"items-can-be-upgraded-using-ai\"\u003eItems can be upgraded using AI\u003c/h2\u003e\n\u003cp\u003eGitHub Copilot App Modernization - Upgrade for .NET is a powerful Visual Studio extension that works with you to upgrade projects to newer versions of .NET, upgrade dependencies, and apply code fixes.\u003c/p\u003e\n\u003cp\u003eGitHub Copilot application modernization is distributed as a Visual Studio extension and is an interactive upgrade process.\u003c/p\u003e","title":"Zero Framework upgraded to Aspire 9.3 version"}]