C# Programming Labs

Master enterprise patterns: dependency injection, HTTP client usage, and expression trees for dynamic code.

DI, HTTP Clients & Expressions - Module 9

Build scalable, testable applications with modern .NET patterns.

Lab 25: Dependency Injection Basics
Expert
Coding Challenge
Your Task: Implement dependency injection pattern manually, then use Microsoft's DI container to register and resolve services.

Detailed Requirements:
1. Define interfaces and implementations: public interface ILogger { void Log(string message); } public interface IDataService { string GetData(); } public class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine($"[LOG] {message}"); } public class DataService : IDataService { private readonly ILogger _logger; public DataService(ILogger logger) { _logger = logger; } public string GetData() { _logger.Log("Fetching data"); return "Hello from DataService"; } } 2. Register services with ServiceCollection: var services = new ServiceCollection(); services.AddSingleton<ILogger, ConsoleLogger>(); services.AddTransient<IDataService, DataService>(); 3. Build ServiceProvider and resolve: var provider = services.BuildServiceProvider(); var dataService = provider.GetRequiredService<IDataService>(); 4. Understand lifetimes: Singleton, Scoped, Transient.

💡 Pro Tips:
• Depend on abstractions (interfaces), not concrete types
• Constructor injection is preferred over property injection
• Singleton = one instance; Scoped = one per scope; Transient = new each time
• Requires Microsoft.Extensions.DependencyInjection NuGet

Expected Output: [LOG] Fetching data Result: Hello from DataService Same singleton? True Same transient? False

Requirements Checklist

Define ILogger and IDataService interfaces
Implement classes with constructor injection
Create ServiceCollection and register services
Build ServiceProvider
Resolve services with GetRequiredService
Demonstrate singleton vs transient lifetimes
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Interface: public interface ILogger { void Log(string msg); }
• Register: services.AddSingleton<ILogger, ConsoleLogger>();
• Build: var provider = services.BuildServiceProvider();
• Resolve: provider.GetRequiredService<IDataService>();
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 26: HttpClient & REST APIs
Expert
Coding Challenge
Your Task: Use HttpClient to make GET and POST requests, handle responses, and deserialize JSON.

Detailed Requirements:
1. Create and configure HttpClient: using var client = new HttpClient(); client.BaseAddress = new Uri("https://api.example.com/"); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 2. Make GET request: var response = await client.GetAsync("users/1"); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); var user = JsonSerializer.Deserialize<User>(json); 3. Make POST request with JSON body: var newUser = new User { Name = "Alice" }; var content = new StringContent(JsonSerializer.Serialize(newUser), Encoding.UTF8, "application/json"); var postResponse = await client.PostAsync("users", content); 4. Handle errors: Check status codes, use try/catch.

💡 Pro Tips:
• Use IHttpClientFactory in real apps (avoids socket exhaustion)
• Always dispose HttpClient or use singleton/factory pattern
• Use response.EnsureSuccessStatusCode() to throw on errors
• ReadFromJsonAsync<T>() simplifies deserialization

Expected Output: GET /users/1 - Status: 200 OK User: { Id: 1, Name: "Alice" } POST /users - Status: 201 Created Created user ID: 2

Requirements Checklist

Create HttpClient with BaseAddress
Make async GET request
Deserialize JSON response
Make POST request with JSON body
Check response status codes
Handle errors appropriately
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Create: using var client = new HttpClient();
• GET: await client.GetAsync("endpoint")
• POST: await client.PostAsync("endpoint", content)
• Content: new StringContent(json, Encoding.UTF8, "application/json")
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 27: Expression Trees
Expert
Coding Challenge
Your Task: Build and compile expression trees to create dynamic code at runtime.

Detailed Requirements:
1. Create simple expression from lambda: Expression<Func<int, int, int>> addExpr = (a, b) => a + b; var compiled = addExpr.Compile(); int result = compiled(3, 5); // 8 2. Build expression tree manually: var paramX = Expression.Parameter(typeof(int), "x"); var paramY = Expression.Parameter(typeof(int), "y"); var body = Expression.Add(paramX, paramY); var lambda = Expression.Lambda<Func<int, int, int>>(body, paramX, paramY); var func = lambda.Compile(); 3. Inspect expression tree: Walk the tree structure.
4. Create dynamic property accessor: // Build: x => x.PropertyName var param = Expression.Parameter(typeof(T), "x"); var property = Expression.Property(param, propertyName); var lambda = Expression.Lambda(property, param);

💡 Pro Tips:
• Expression trees represent code as data structures
• Used by LINQ providers (EF Core) to translate to SQL
• Expression.Lambda wraps body into callable delegate
• Compiled expressions are as fast as regular code

Expected Output: Lambda from code: 3 + 5 = 8 Manual expression: 10 * 4 = 40 Expression type: Lambda Body type: Add Dynamic getter: Name = Alice

Requirements Checklist

Create Expression from lambda syntax
Compile expression to delegate
Build expression tree manually with Expression API
Use Expression.Parameter and Expression.Add/Multiply
Inspect expression tree properties
Create dynamic property accessor
Output
// Click "Run Code" to compile and execute
Hints & Tips
• From lambda: Expression<Func<int,int>> e = x => x * 2;
• Compile: var func = expression.Compile();
• Manual: Expression.Parameter, Expression.Add
• Lambda: Expression.Lambda<Func<...>>(body, params)
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below