Advanced Cross-Platform Application Programming With .NET - Lập trình ứng dụng đa nền tảng nâng cao với .NET
PRN222 - Lập trình ứng dụng đa nền tảng nâng cao với .NET dạy cách dựng ứng dụng web chạy trên nhiều nền tảng bằng C# 12 và .NET 8 với ASP.NET Core. Môn đi từ chỗ hai tiến trình nói chuyện được với nhau, qua cách làm cho một máy chủ phục vụ nhiều người cùng lúc mà không đứng hình, tới các mô hình dựng giao diện và các dịch vụ chạy ngầm phía sau. Lớp dưới cùng là lập trình mạng trong .NET với địa chỉ IP, hostname, DNS, URI, các giao thức TCP và UDP, TcpListener cùng AcceptSocketAsync, TcpClient, Socket, lớp Stream, HttpClient và GetAsync. Lớp kế là lập trình bất đồng bộ và song song: async/await, Task, Task.Wait, Task.WhenAll, không gian tên System.Threading.Tasks, Parallel.ForEach, Task Parallel Library, PLINQ với AsParallel, cùng ranh giới giữa tác vụ I/O-bound và tác vụ CPU-bound. Phần kiến trúc dựng Dependency Injection và Inversion of Control trên nền nguyên lý SOLID, với AddScoped, AddTransient, AddSingleton và các vòng đời service, constructor injection, property injection, IServiceProvider, Dependency Inversion và Open Closed. Trên nền đó là ba lối dựng giao diện web. ASP.NET Core MVC tách controller, action, IActionResult, ViewResult, HttpPost, Bind, Compare, directive @model, cú pháp Razor, ViewData và ViewBag, PartialView, thư mục wwwroot, model binding và model validation qua ModelState. Razor Pages gộp trang với mã xử lý: file .cshtml, thư mục Pages, lớp PageModel, handler OnGet và OnPost, AddRazorPages, BindProperty, _Layout.cshtml, TempData, Session, Cookies, tiêm phụ thuộc vào PageModel và kiểm tra dữ liệu phía client bằng jQuery Unobtrusive Validation. Blazor đưa C# xuống trình duyệt theo ý niệm C# Everywhere, với Blazor Server, Blazor WebAssembly, hai chế độ render, component, App.razor, directive @page, @bind, @inject, khối @code và thuộc tính Parameter. Hai mảng cuối lo phần chạy ngầm: giao tiếp thời gian thực bằng SignalR với lớp Hub, IHubContext, Clients, Groups.AddToGroupAsync, Hub filters, MessagePack, tự động kết nối lại, AddSignalR, Context và User; cùng Worker Service với BackgroundService, IHostedService, Microsoft.Extensions.Hosting, ExecuteAsync, StartAsync, StopAsync, CancellationToken, cấu hình trong appsettings.json và xử lý hàng đợi. Dữ liệu đi xuyên suốt các lớp đó qua Entity Framework Core và DbContext. Học xong, người học dựng được một ứng dụng .NET nhiều tầng, chọn đúng mô hình giao diện cho từng bài toán và xử lý được phần việc chạy nền lẫn phần đẩy dữ liệu tức thời.
Để lắng nghe các kết nối TCP vào một cổng cụ thể, bạn cần sử dụng lớp .NET TcpListener. TcpListener cho phép bạn tạo một máy chủ TCP và lắng nghe các yêu cầu kết nối từ các máy khách. Khi một máy khách kết nối đến máy chủ, bạn có thể sử dụng phương thức Accept để chấp nhận kết nối và bắt đầu giao tiếp với máy khách. TcpListener cũng cung cấp các phương thức để gửi và nhận dữ liệu qua TCP, giúp bạn xây dựng các ứng dụng mạng phức tạp. Ngoài ra, TcpListener cũng hỗ trợ các tính năng như xác thực và mã hóa để đảm bảo an toàn cho các giao tiếp mạng.
public IActionResult Save(Person person){if(ModelState.IsValid) return Content("Valid");
return Content("Error");}
If no value for Age is passed and Age is int, what happens?
AValid
BError
CException
DNull
Lời giải
Nếu không có giá trị cho thuộc tính Age và Age được định nghĩa là kiểu int, thì khi thực hiện phương thức Save, chương trình sẽ không ném ra ngoại lệ. Thay vào đó, nó sẽ kiểm tra trạng thái hợp lệ của mô hình (ModelState.IsValid). Vì Age không được cung cấp, nên mô hình sẽ không hợp lệ. Do đó, phương thức sẽ trả về nội dung "Error".
public class Person {
public int Age { get; set;}}
public IActionResult Save(Person person){if(!ModelState.IsValid){
return Content("Invalid!");}
return Content("OK");}
Input: Age = "abc"? What is the output?
AOK
BInvalid!
CException
DNull
Lời giải
Khi thực hiện việc lưu trữ thông tin người dùng với lớp Person, chúng ta cần kiểm tra xem dữ liệu đầu vào có hợp lệ hay không. Trong trường hợp này, lớp Person chỉ có một thuộc tính là Age, và dữ liệu đầu vào là một chuỗi 'abc'. Vì Age được định nghĩa là một số nguyên (int), nên khi chúng ta cố gắng lưu trữ chuỗi 'abc' vào thuộc tính Age, hệ thống sẽ phát hiện ra lỗi và trả về thông điệp 'Invalid!' để thông báo cho người dùng.
Để tạo một View được định nghĩa rõ ràng (strongly-typed View), bạn cần sử dụng một trong các chỉ thị sau. Trong đó, chỉ thị '@model' được sử dụng rộng rãi nhất để khai báo kiểu dữ liệu của View. Ví dụ, nếu bạn muốn tạo một View để hiển thị thông tin về một người dùng, bạn có thể sử dụng '@model' để khai báo kiểu dữ liệu là 'User'. Điều này giúp cho View của bạn được định nghĩa rõ ràng và dễ dàng sử dụng.
public IActionResult Index(){
ViewData["User"]="Admin";
return View();}
Razor: <p>@ViewBag.User</p> What is displayed?
AAdmin
Bnull
CError
DNothing
Lời giải
Khi chạy mã C# trên, chúng ta sẽ thấy kết quả là "Admin". Lý do là vì trong phương thức Index(), chúng ta đã gán giá trị "Admin" vào ViewData với khóa "User". Sau đó, trong Razor, chúng ta sử dụng ViewBag.User để hiển thị giá trị này. Vì vậy, kết quả cuối cùng sẽ là "Admin".
IActionResult là một giao diện trong ASP.NET Core, đại diện cho kết quả của một phương thức hành động. Nó cho phép bạn trả về nhiều loại kết quả khác nhau, chẳng hạn như một trang web, một chuyển hướng, một trạng thái HTTP, v.v. Khi một phương thức hành động được gọi, nó sẽ trả về một đối tượng thực thi IActionResult, đại diện cho kết quả của phương thức đó. Điều này cho phép bạn xử lý kết quả một cách linh hoạt và dễ dàng trong ứng dụng của mình.
BThe process of ensuring that user input meets specific criteria.
CThe process of managing database connections.
DThe process of routing HTTP requests.
Lời giải
Trong ASP.NET Core MVC, "Model Validation" là quá trình đảm bảo rằng dữ liệu được nhập vào bởi người dùng đáp ứng các tiêu chí cụ thể. Quá trình này giúp ngăn chặn các dữ liệu không hợp lệ được lưu trữ vào cơ sở dữ liệu hoặc sử dụng trong ứng dụng. Model Validation thường được thực hiện bằng cách sử dụng các thuộc tính và phương thức đặc biệt trên mô hình dữ liệu (model) để xác minh tính hợp lệ của dữ liệu. Ví dụ, bạn có thể sử dụng thuộc tính `[Required]` để yêu cầu một trường dữ liệu không được bỏ trống, hoặc thuộc tính `[Range]` để xác định phạm vi giá trị cho một trường dữ liệu. Quá trình này giúp đảm bảo rằng dữ liệu được nhập vào là chính xác và đáng tin cậy, từ đó giúp ứng dụng hoạt động ổn định và hiệu quả.
BBy adding the service as a constructor parameter.
CBy using global variables.
DBy using static methods.
Lời giải
Để chèn một dịch vụ vào một Razor PageModel, bạn cần sử dụng một trong các phương pháp sau. Phương pháp chính xác là thêm dịch vụ vào tham số constructor. Điều này cho phép bạn sử dụng dịch vụ trong các phương thức của PageModel. Ví dụ, bạn có thể thêm dịch vụ vào constructor như sau:
Lời giải
public class MyPageModel : PageModel {
private readonly MyService _myService;
public MyPageModel(MyService myService){
_myService = myService;}
public void OnGet(){
_myService.DoSomething();}}
Sau đó, bạn cần thêm dịch vụ vào DI container trong Startup.cs.
Lời giải
services.AddTransient<MyService>();
Điều này cho phép bạn sử dụng dịch vụ trong PageModel.
Câu hỏi này yêu cầu bạn chọn file extension phù hợp cho các file Razor Page. Razor Page là một loại file trong ASP.NET Core, được sử dụng để tạo các trang web động. Câu trả lời đúng là .cshtml, vì nó là file extension tiêu chuẩn cho các file Razor Page. Các file .cshtml chứa mã C# và HTML, giúp tạo ra các trang web động phức tạp. Các file .razor cũng có thể được sử dụng, nhưng nó không phải là file extension mặc định. Các file .aspx và .html không phù hợp cho các file Razor Page.
BBy injecting dependencies into the PageModel constructor.
CBy using global variables.
DBy using static methods.
Lời giải
Tùy thuộc vào Razor Pages, Dependency Injection thường được sử dụng bằng cách chèn các phụ thuộc vào constructor của PageModel. Điều này cho phép bạn tách biệt logic kinh doanh khỏi logic trang web, giúp dễ dàng bảo trì và mở rộng ứng dụng. Khi bạn sử dụng Dependency Injection, bạn có thể dễ dàng thay thế các phụ thuộc bằng các phiên bản mới hoặc các phiên bản khác mà không cần thay đổi mã nguồn của trang web. Điều này giúp tăng cường tính linh hoạt và khả năng mở rộng của ứng dụng.
BA dictionary for passing data from PageModel to Razor Page
CA type of middleware
DA static variable shared by all pages
Lời giải
Trong Razor Pages, ViewData là một đối tượng được sử dụng để truyền dữ liệu từ PageModel sang Razor Page. Nó được định nghĩa là một dictionary, cho phép bạn lưu trữ và truy xuất dữ liệu một cách dễ dàng. Khi bạn muốn truyền dữ liệu từ PageModel sang Razor Page, bạn có thể sử dụng ViewData để lưu trữ dữ liệu và sau đó truy xuất nó trong Razor Page. Điều này giúp bạn có thể chia sẻ dữ liệu giữa các phần của ứng dụng một cách dễ dàng. Ví dụ, bạn có thể sử dụng ViewData để lưu trữ dữ liệu người dùng và sau đó sử dụng nó để hiển thị thông tin người dùng trên Razor Page.
Trong Razor Pages, [BindProperty] là một thuộc tính quan trọng giúp liên kết dữ liệu từ form với các thuộc tính của PageModel. Khi bạn sử dụng [BindProperty] trên một thuộc tính của PageModel, bạn có thể tự động liên kết dữ liệu từ form với thuộc tính đó. Điều này giúp giảm thiểu mã code và tăng hiệu suất khi xử lý dữ liệu từ form. Ví dụ, nếu bạn có một form với trường nhập tên và địa chỉ, bạn có thể sử dụng [BindProperty] để liên kết dữ liệu từ form với các thuộc tính tên và địa chỉ của PageModel. Điều này giúp bạn dễ dàng truy cập và xử lý dữ liệu từ form trong code-behind của trang Razor Pages.
Trong Razor Pages, xác thực ở phía khách hàng được kích hoạt bằng cách sử dụng jQuery Unobtrusive Validation. Điều này cho phép thực hiện xác thực trên trình duyệt mà không cần phải gửi yêu cầu đến máy chủ. Để sử dụng tính năng này, bạn cần thêm thư viện jQuery Unobtrusive Validation vào dự án của mình. Sau đó, bạn có thể sử dụng các thuộc tính dữ liệu (data attributes) để chỉ định các quy tắc xác thực cho các trường nhập liệu. Khi người dùng nhập dữ liệu vào các trường này, trình duyệt sẽ tự động thực hiện xác thực và hiển thị thông báo lỗi nếu dữ liệu không hợp lệ. Điều này giúp cải thiện trải nghiệm người dùng và giảm tải cho máy chủ.
DI (Dependency Injection) trong Razor Pages nhấn mạnh nguyên tắc không lặp lại (Don't Repeat Yourself, DRY). Nguyên tắc này cho phép giảm thiểu sự lặp lại mã nguồn và tăng cường khả năng tái sử dụng. DI giúp bạn tách biệt các phụ thuộc và cung cấp chúng một cách linh hoạt, từ đó giảm thiểu sự lặp lại mã nguồn và tăng cường tính linh hoạt của ứng dụng. Điều này giúp bạn dễ dàng thay đổi hoặc cập nhật các phụ thuộc mà không ảnh hưởng đến các phần khác của ứng dụng.
BPerforming scheduled tasks or background processing.
CRendering web pages.
DManaging user authentication.
Lời giải
Một Worker Service là một loại ứng dụng trong hệ thống phân tán, được thiết kế để thực hiện các tác vụ nền hoặc xử lý dữ liệu trong nền. Nó thường được sử dụng để thực hiện các nhiệm vụ lặp đi lặp lại, như gửi email hàng ngày, cập nhật dữ liệu trên cơ sở dữ liệu, hoặc thực hiện các tác vụ khác mà không cần tương tác trực tiếp với người dùng. Trong trường hợp này, câu trả lời đúng là "Thực hiện các tác vụ lặp đi lặp lại hoặc xử lý dữ liệu trong nền".
BBy overriding the ExecuteAsync(CancellationToken) method in a class derived from BackgroundService.
CBy using static HTML files.
DBy using database queries.
Lời giải
Để thực hiện một nhiệm vụ nền (background task) bằng cách sử dụng dịch vụ Worker, bạn cần tạo một lớp kế thừa từ BackgroundService. Lớp này sẽ chứa phương thức ExecuteAsync, nơi bạn thực hiện logic của nhiệm vụ nền. Phương thức này sẽ được gọi tự động bởi dịch vụ Worker khi nó được khởi tạo. Bạn cũng có thể sử dụng phương thức StartAsync để bắt đầu nhiệm vụ nền ngay lập tức. Ngoài ra, bạn có thể sử dụng các thuộc tính và phương thức khác của lớp BackgroundService để quản lý và giám sát nhiệm vụ nền.
CancellationToken là một tham số quan trọng trong phương thức ExecuteAsync(CancellationToken) trong lập trình. Mục đích chính của tham số này là cho phép dịch vụ được dừng một cách êm ái (gracefully stopped) khi cần thiết. Điều này có nghĩa là khi dịch vụ cần được dừng lại, tham số CancellationToken sẽ được đặt thành trạng thái đã hủy (canceled), từ đó cho phép dịch vụ thực hiện các hành động cần thiết để dừng lại một cách an toàn và không gây ra lỗi. Ví dụ, dịch vụ có thể thoát khỏi các nguồn hệ thống, đóng các kết nối mạng, hoặc thực hiện các hành động khác cần thiết để dừng lại một cách êm ái. Mục đích của CancellationToken là đảm bảo rằng dịch vụ được dừng lại một cách an toàn và không gây ra lỗi khi cần thiết.
public class Worker : BackgroundService {
protected override Task ExecuteAsync(CancellationToken stoppingToken){
return Task.CompletedTask;}}
What is ExecuteAsync?
AThe entry point for background execution.
BThe constructor.
CA logging method.
DA thread initializer.
Lời giải
Trong mã nguồn C#, ExecuteAsync là phương thức được sử dụng để thực hiện các tác vụ nền (background tasks). Nó là phương thức được gọi khi một đối tượng thực hiện các tác vụ nền (background service) được tạo ra. Phương thức này được sử dụng để thực hiện các tác vụ nền một cách độc lập và không ảnh hưởng đến luồng chính của chương trình. Trong ví dụ trên, Worker là một lớp thực hiện các tác vụ nền và ExecuteAsync là phương thức được sử dụng để thực hiện các tác vụ nền này. Phương thức ExecuteAsync được gọi khi Worker được tạo ra và nó sẽ thực hiện các tác vụ nền một cách độc lập.
Các kịch bản phù hợp nhất cho Worker Services là các trường hợp xử lý hàng đợi (queue messages). Worker Services được thiết kế để chạy trong nền và xử lý các tác vụ lặp đi lặp lại, chẳng hạn như xử lý hàng đợi. Điều này giúp giảm tải cho ứng dụng web và cải thiện hiệu suất. Ngoài ra, Worker Services cũng có thể được sử dụng để chạy các tác vụ độc lập, chẳng hạn như gửi email hoặc thông báo. Tuy nhiên, các kịch bản như chạy UI Blazor, hiển thị Razor Pages hoặc chỉ hosting REST APIs không phù hợp với Worker Services vì chúng yêu cầu tương tác trực tiếp với người dùng hoặc yêu cầu hiển thị UI.
Phương thức ExecuteAsync(CancellationToken) trong lớp BackgroundService có vai trò quan trọng trong việc định nghĩa logic chính của nhiệm vụ chạy nền. Phương thức này được gọi khi BackgroundService được khởi tạo và sẽ chạy liên tục cho đến khi ứng dụng bị dừng. Trong phương thức này, bạn có thể thực hiện các tác vụ như đọc dữ liệu từ cơ sở dữ liệu, xử lý dữ liệu, gửi thông báo, v.v. Mục đích chính của phương thức này là thực hiện các tác vụ nền mà không ảnh hưởng đến giao diện người dùng. Ví dụ, bạn có thể sử dụng phương thức này để gửi thông báo hàng ngày hoặc hàng giờ, hoặc để cập nhật dữ liệu trên cơ sở dữ liệu.