.NET Basics
.NET CLI Commands
Bash
# Create new project
dotnet new console -n MyApp # Console app
dotnet new webapi -n MyApi # Web API
dotnet new mvc -n MyMvc # MVC app
dotnet new blazorserver -n MyBlazor # Blazor Server
dotnet new classlib -n MyLib # Class library
# Run project
dotnet run
dotnet watch run # Hot reload
# Build
dotnet build
dotnet publish -c Release
# Package management
dotnet add package Newtonsoft.Json
dotnet remove package PackageName
dotnet restore
# Solution management
dotnet new sln -n MySolution
dotnet sln add ./MyProject/MyProject.csproj
# Entity Framework
dotnet tool install --global dotnet-ef
dotnet ef migrations add InitialCreate
dotnet ef database update
Project Structure
Folder Structure
MyWebApi/
├── Controllers/
│ └── WeatherController.cs
├── Models/
│ └── Weather.cs
├── Services/
│ └── WeatherService.cs
├── Data/
│ └── AppDbContext.cs
├── DTOs/
│ └── WeatherDto.cs
├── appsettings.json
├── appsettings.Development.json
├── Program.cs
└── MyWebApi.csproj
ASP.NET Core
Minimal API (Program.cs)
C#
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Middleware pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// Minimal API endpoints
app.MapGet("/", () => "Hello World!");
app.MapGet("/items", () => new[] { "Item1", "Item2" });
app.MapGet("/items/{id}", (int id) => $"Item {id}");
app.MapPost("/items", (Item item) => Results.Created($"/items/{item.Id}", item));
app.MapPut("/items/{id}", (int id, Item item) => Results.NoContent());
app.MapDelete("/items/{id}", (int id) => Results.Ok());
app.Run();
record Item(int Id, string Name);
Middleware
C#
// Custom middleware
app.Use(async (context, next) =>
{
// Before request
Console.WriteLine($"Request: {context.Request.Path}");
await next(); // Call next middleware
// After request
Console.WriteLine($"Response: {context.Response.StatusCode}");
});
// Custom middleware class
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Log request
await _next(context);
// Log response
}
}
// Register: app.UseMiddleware();
Web API Controllers
C#
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
// GET api/products
[HttpGet]
public async Task<ActionResult<IEnumerable<Product>>> GetAll()
{
var products = await _productService.GetAllAsync();
return Ok(products);
}
// GET api/products/5
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetById(int id)
{
var product = await _productService.GetByIdAsync(id);
if (product == null)
return NotFound();
return Ok(product);
}
// POST api/products
[HttpPost]
public async Task<ActionResult<Product>> Create([FromBody] CreateProductDto dto)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var product = await _productService.CreateAsync(dto);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
// PUT api/products/5
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateProductDto dto)
{
await _productService.UpdateAsync(id, dto);
return NoContent();
}
// DELETE api/products/5
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
await _productService.DeleteAsync(id);
return NoContent();
}
}
Data Transfer Objects (DTOs)
C#
public record CreateProductDto(
[Required] string Name,
[Range(0, 10000)] decimal Price,
string? Description
);
public record ProductDto(int Id, string Name, decimal Price);
Entity Framework Core
DbContext
C#
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Fluent API configuration
modelBuilder.Entity<Product>()
.HasKey(p => p.Id);
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.IsRequired()
.HasMaxLength(100);
modelBuilder.Entity<Product>()
.HasOne(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId);
}
}
// Entity
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
// Register in Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
CRUD Operations
C#
public class ProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
// Read
public async Task<List<Product>> GetAllAsync()
{
return await _context.Products
.Include(p => p.Category)
.ToListAsync();
}
public async Task<Product?> GetByIdAsync(int id)
{
return await _context.Products
.FirstOrDefaultAsync(p => p.Id == id);
}
// Create
public async Task<Product> CreateAsync(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return product;
}
// Update
public async Task UpdateAsync(Product product)
{
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
// Delete
public async Task DeleteAsync(int id)
{
var product = await _context.Products.FindAsync(id);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
}
// LINQ Queries
public async Task<List<Product>> SearchAsync(string term)
{
return await _context.Products
.Where(p => p.Name.Contains(term))
.OrderBy(p => p.Price)
.Take(10)
.ToListAsync();
}
}
Dependency Injection
C#
// Interface
public interface IEmailService
{
Task SendEmailAsync(string to, string subject, string body);
}
// Implementation
public class EmailService : IEmailService
{
private readonly ILogger<EmailService> _logger;
public EmailService(ILogger<EmailService> logger)
{
_logger = logger;
}
public async Task SendEmailAsync(string to, string subject, string body)
{
_logger.LogInformation($"Sending email to {to}");
// Send email logic
}
}
// Register services in Program.cs
builder.Services.AddScoped<IEmailService, EmailService>();
builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();
// Lifetimes:
// Transient - Created each time requested
// Scoped - Created once per HTTP request
// Singleton - Created once for application lifetime
// Use in controller
public class NotificationController : ControllerBase
{
private readonly IEmailService _emailService;
public NotificationController(IEmailService emailService)
{
_emailService = emailService;
}
}
Authentication & Authorization
JWT Authentication
C#
// Program.cs - Configure JWT
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
builder.Services.AddAuthorization();
// Middleware
app.UseAuthentication();
app.UseAuthorization();
// Generate token
public string GenerateToken(User user)
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Role, user.Role)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.Now.AddHours(1),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
// Protect endpoints
[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile() { }
[Authorize(Roles = "Admin")]
[HttpDelete("{id}")]
public IActionResult Delete(int id) { }
Unit Testing
C#
using Xunit;
using Moq;
public class ProductServiceTests
{
private readonly Mock<IProductRepository> _mockRepo;
private readonly ProductService _service;
public ProductServiceTests()
{
_mockRepo = new Mock<IProductRepository>();
_service = new ProductService(_mockRepo.Object);
}
[Fact]
public async Task GetById_ExistingId_ReturnsProduct()
{
// Arrange
var expectedProduct = new Product { Id = 1, Name = "Test" };
_mockRepo.Setup(r => r.GetByIdAsync(1))
.ReturnsAsync(expectedProduct);
// Act
var result = await _service.GetByIdAsync(1);
// Assert
Assert.NotNull(result);
Assert.Equal("Test", result.Name);
}
[Fact]
public async Task GetById_NonExistingId_ReturnsNull()
{
// Arrange
_mockRepo.Setup(r => r.GetByIdAsync(999))
.ReturnsAsync((Product?)null);
// Act
var result = await _service.GetByIdAsync(999);
// Assert
Assert.Null(result);
}
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public async Task Delete_ValidId_CallsRepository(int id)
{
// Act
await _service.DeleteAsync(id);
// Assert
_mockRepo.Verify(r => r.DeleteAsync(id), Times.Once);
}
}
.NET Best Practices:
• Use async/await for I/O operations
• Follow Repository pattern for data access
• Use DTOs to separate API models from entities
• Implement proper error handling middleware
• Use configuration for environment-specific settings
• Use async/await for I/O operations
• Follow Repository pattern for data access
• Use DTOs to separate API models from entities
• Implement proper error handling middleware
• Use configuration for environment-specific settings