Exercise: ASP.NET Core Blazor Dependency Injection (DI)
Exercise Set 2: ASP.NET Core Blazor Dependency Injection (DI)
Goal: Understand how to register services for DI and consume them in Blazor components, specifically using the ProductDataService from the previous exercise.
Scenario: Refactor the "Product Catalog" to use DI for the ProductDataService instead of directly instantiating it.
Instructions:
Part 1: Registering and Injecting a Singleton Service
Continue from Exercise Set 1. If you closed it, open BlazorRoutingExercise.
1. Modify ProductDataService to be an Interface:
- Rename
Data/ProductDataService.cstoData/IProductDataService.cs. - Change
class ProductDataServicetointerface IProductDataService. - Remove the constructor and the private
_productsfield. - Your
IProductDataService.csshould look like this:
Data/IProductDataService.cs:
namespace BlazorRoutingExercise.Data { public interface IProductDataService { List<Product> GetAllProducts(); Product? GetProductById(int id); } }
2. Create the Concrete Implementation:
- In the
Datafolder, create a new class file namedProductDataService.cs. (Yes, same name as the original file, but this is the implementation). - Make this class implement
IProductDataServiceand move the_productslist and methods into it. - Your
ProductDataService.csshould look like this:
Data/ProductDataService.cs:
using BlazorRoutingExercise.Models; using System.Collections.Generic; using System.Linq; namespace BlazorRoutingExercise.Data { // This class implements the interface public class ProductDataService : IProductDataService { private List<Product> _products = new List<Product> { new Product { Id = 1, Name = "Laptop Pro", Description = "Powerful laptop for professionals.", Price = 1200.00M }, new Product { Id = 2, Name = "Gaming Mouse", Description = "Ergonomic mouse for serious gamers.", Price = 75.50M }, new Product { Id = 3, Name = "Wireless Keyboard", Description = "Full-size, quiet wireless keyboard.", Price = 99.99M }, new Product { Id = 4, Name = "Monitor UltraWide", Description = "Immersive ultrawide display for productivity.", Price = 450.00M } }; public List<Product> GetAllProducts() => _products; public Product? GetProductById(int id) => _products.FirstOrDefault(p => p.Id == id); } }
3. Register the Service in Program.cs:
- Open
Program.cs. - Add a
usingstatement forBlazorRoutingExercise.Data. - Register
IProductDataServicewith its implementationProductDataServiceas aSingleton.
Program.cs (relevant section):
// In Program.cs, within the 'Add services to the container.' section: builder.Services.AddSingleton<BlazorRoutingExercise.Data.IProductDataService, BlazorRoutingExercise.Data.ProductDataService>();
Why Singleton? For this simple data service, a Singleton means all components (across all user connections) will share the same instance of the product list. If a product were added/removed, all users would see the change (not applicable in this read-only demo, but good to know).
4. Inject the Service into ProductList.razor:
- Open
Pages/ProductList.razor. - Remove the line
private ProductDataService _productService = new ProductDataService();. - Add the
@injectdirective at the top, specifying the interface:
Pages/ProductList.razor (relevant section):
@inject BlazorRoutingExercise.Data.IProductDataService ProductService // Injected via DI
- In the
OnInitializedmethod, change_productService.GetAllProducts();toProductService.GetAllProducts();.
5. Inject the Service into ProductDetails.razor:
- Open
Pages/ProductDetails.razor. - Remove the line
private ProductDataService _productService = new ProductDataService();. - Add the
@injectdirective at the top, specifying the interface:
Pages/ProductDetails.razor (relevant section):
@inject BlazorRoutingExercise.Data.IProductDataService ProductService // Injected via DI
- In the
OnParametersSetmethod, change_productService.GetProductById(ProductId);toProductService.GetProductById(ProductId);.
6. Test Your DI:
- Run the application.
- Verify that the "Products" list and "Product Details" pages still load correctly. If they do, it means your service is being correctly injected and used!
Part 2: Challenge - Explore Service Lifetimes
1. Experiment with Lifetimes:
- In
Program.cs, change the service registration fromAddSingletontoAddScoped:
Program.cs (relevant section):
builder.Services.AddScoped<BlazorRoutingExercise.Data.IProductDataService, BlazorRoutingExercise.Data.ProductDataService>();
- Run the application. Open two different browser tabs.
- Question: For this read-only
ProductDataService, do you observe any difference betweenSingletonandScopedbehavior when viewing products in different tabs? (Hint: No, because the data isn't changing. The difference matters more when services hold state that can be modified per user/request). - Optional: Change it to
AddTransient. Again, for a read-only service, the visual difference is minimal.
2. Challenge (Conceptual):
If you had a ShoppingCartService that stored items for a single user's shopping cart, which lifetime (Singleton, Scoped, or Transient) would be most appropriate? Why?
Self-check answer: Scoped would be most appropriate. Each user (connection/request) would get their own unique shopping cart instance, but that instance would be reused for all component interactions within that same user's session.
Comments
Post a Comment