What Is .NET Core?
.NET Core is an open-source, cross-platform framework developed by Microsoft. It lets you build web applications, APIs, desktop apps, and more using C#.
Here's a quick timeline to avoid confusion:
.NET Framework (2002) — Windows-only, the original
.NET Core (2016) — Cross-platform rewrite, faster, modern
.NET 5, 6, 7, 8, 9 — Microsoft unified everything under just ".NET" — so when you see ".NET 8", that's the modern .NET Core
When a job says ".NET Core" they almost always mean the modern cross-platform .NET (5+). The concepts are the same.
Setting Up
# Check your .NET version
dotnet --version
# Create a new Web API project
dotnet new webapi -n MyApi
cd MyApi
dotnet run
Your API will be running at https://localhost:5001. Open your browser and go to /swagger — you'll see an interactive API docs page automatically generated for you.
Project Structure
When you create a new Web API project, here's what you get:
MyApi/
├── Controllers/
│ └── WeatherForecastController.cs
├── Properties/
│ └── launchSettings.json
├── appsettings.json
├── appsettings.Development.json
├── Program.cs
└── MyApi.csproj
The important files:
Program.cs— entry point, where you configure services and middlewareControllers/— where your API endpoints liveappsettings.json— configuration (connection strings, app settings).csproj— project file listing your dependencies (like package.json in Node)
1. Program.cs — The Heart of Your App
In modern .NET (6+), Program.cs is clean and minimal:
var builder = WebApplication.CreateBuilder(args);
// Register services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure middleware pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Think of this file in two parts:
Services (
builder.Services) — what your app can doMiddleware (
app.Use...) — how requests flow through your app
2. Controllers and Routing
A controller handles incoming HTTP requests and returns responses.
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// GET /api/products
[HttpGet]
public IActionResult GetAll()
{
var products = new List<string> { "Laptop", "Phone", "Tablet" };
return Ok(products);
}
// GET /api/products/1
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
if (id <= 0)
return BadRequest("Invalid ID.");
return Ok(new { Id = id, Name = "Laptop" });
}
// POST /api/products
[HttpPost]
public IActionResult Create([FromBody] ProductDto product)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// Save to database...
return CreatedAtAction(nameof(GetById), new { id = 1 }, product);
}
// PUT /api/products/1
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] ProductDto product)
{
// Update logic...
return NoContent();
}
// DELETE /api/products/1
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
// Delete logic...
return NoContent();
}
}
HTTP Status Codes — know these:
Method Success Response Ok(data) 200 — request succeeded CreatedAtAction(...) 201 — resource created NoContent() 204 — success, no body BadRequest(...) 400 — bad input Unauthorized() 401 — not authenticated Forbid() 403 — authenticated but not allowed NotFound() 404 — resource doesn't exist
3. Dependency Injection (DI)
Dependency Injection is one of the most important concepts in .NET — it will come up in every interview. It's how .NET manages your app's dependencies (services, repositories, etc.).
Instead of creating objects manually (new MyService()), you register them once and the framework injects them where needed.
Step 1 — Create a service interface and implementation:
public interface IProductService
{
List<Product> GetAll();
Product? GetById(int id);
}
public class ProductService : IProductService
{
private readonly List<Product> _products = new()
{
new Product { Id = 1, Name = "Laptop", Price = 999.99m },
new Product { Id = 2, Name = "Phone", Price = 699.99m },
};
public List<Product> GetAll() => _products;
public Product? GetById(int id) => _products.FirstOrDefault(p => p.Id == id);
}
Step 2 — Register it in Program.cs:
builder.Services.AddScoped<IProductService, ProductService>();
Step 3 — Inject it into your controller:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public IActionResult GetAll()
{
return Ok(_productService.GetAll());
}
}
Service Lifetimes — interview favourite:
Lifetime Registered with Created Transient AddTransient New instance every time it's requested Scoped AddScoped One instance per HTTP request Singleton AddSingleton One instance for the entire app lifetime
Rule of thumb: Use Scoped for most services (repositories, business logic). Use Singleton for things like configuration or caches. Use Transient for lightweight, stateless utilities.
4. Entity Framework Core (EF Core)
EF Core is the official ORM (Object-Relational Mapper) for .NET. It lets you work with a database using C# objects instead of raw SQL.
Install packages:
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
Define your model:
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public DateTime CreatedAt { get; set; }
}
Create your DbContext:
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
}
Register in Program.cs:
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
Add connection string to appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyDb;Trusted_Connection=True;"
}
}
Migrations — how you create/update database tables:
dotnet ef migrations add InitialCreate
dotnet ef database update
Using EF Core in a service:
public class ProductService : IProductService
{
private readonly AppDbContext _context;
public ProductService(AppDbContext context)
{
_context = context;
}
public async Task<List<Product>> GetAllAsync()
{
return await _context.Products.ToListAsync();
}
public async Task<Product?> GetByIdAsync(int id)
{
return await _context.Products.FindAsync(id);
}
public async Task AddAsync(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var product = await _context.Products.FindAsync(id);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
}
}
5. Configuration and appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Server=...;Database=...;"
},
"AppSettings": {
"JwtSecret": "my-super-secret-key",
"TokenExpiryHours": 24
}
}
Reading config in code:
// In Program.cs or a service
var jwtSecret = builder.Configuration["AppSettings:JwtSecret"];
// Or with options pattern (better)
builder.Services.Configure<AppSettings>(
builder.Configuration.GetSection("AppSettings"));
// Inject IOptions<AppSettings> in a class
public class AuthService
{
private readonly AppSettings _settings;
public AuthService(IOptions<AppSettings> options)
{
_settings = options.Value;
}
}
6. Middleware
Middleware is code that runs on every request, in order. Think of it as a pipeline.
// Custom middleware
app.Use(async (context, next) =>
{
Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");
await next(); // call next middleware
Console.WriteLine($"Response: {context.Response.StatusCode}");
});
Common built-in middleware you'll use:
app.UseHttpsRedirection(); // redirect HTTP to HTTPS
app.UseAuthentication(); // check who the user is
app.UseAuthorization(); // check what the user can do
app.UseCors(); // handle cross-origin requests
app.UseStaticFiles(); // serve static files
Order matters — UseAuthentication must come before UseAuthorization.
7. Authentication with JWT
JWT (JSON Web Token) is the most common authentication mechanism in .NET APIs.
Install package:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Register in Program.cs:
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"]!))
};
});
Protect an endpoint:
[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
return Ok(new { UserId = userId });
}
// Allow specific roles
[Authorize(Roles = "Admin")]
[HttpDelete("{id}")]
public IActionResult Delete(int id) { ... }
8. Data Validation
Use Data Annotations on your DTOs:
public class CreateProductDto
{
[Required]
[StringLength(100, MinimumLength = 2)]
public string Name { get; set; } = string.Empty;
[Range(0.01, 100000)]
public decimal Price { get; set; }
[Required]
[EmailAddress]
public string ContactEmail { get; set; } = string.Empty;
}
With [ApiController] on your controller, validation happens automatically — if the model is invalid, it returns a 400 response without you writing any extra code.
9. CORS
If your frontend (React, Angular, etc.) is on a different domain, you need CORS:
// In Program.cs
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.WithOrigins("https://myapp.com", "http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
app.UseCors("AllowFrontend");
10. The Repository Pattern
In real projects, you don't call EF Core directly from controllers. You add a layer called a repository — it separates database logic from business logic.
// Interface
public interface IProductRepository
{
Task<List<Product>> GetAllAsync();
Task<Product?> GetByIdAsync(int id);
Task AddAsync(Product product);
Task UpdateAsync(Product product);
Task DeleteAsync(int id);
}
// Implementation
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public async Task<List<Product>> GetAllAsync() =>
await _context.Products.ToListAsync();
public async Task<Product?> GetByIdAsync(int id) =>
await _context.Products.FindAsync(id);
public async Task AddAsync(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(Product product)
{
_context.Products.Update(product);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var product = await _context.Products.FindAsync(id);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
}
}
// Register
builder.Services.AddScoped<IProductRepository, ProductRepository>();
Interview Cheat Sheet
Topic What to say What is middleware? Code that runs on every HTTP request in a pipeline. Order matters. DI lifetimes Transient = new every time, Scoped = per request, Singleton = once ever What is EF Core? ORM that maps C# classes to database tables. Use migrations to sync schema. What is a DTO? Data Transfer Object — a simple class used to shape API input/output, separate from domain models Repository pattern Abstracts database access behind an interface. Makes code testable. What is CORS? Browser security mechanism. You configure it to allow specific frontend origins.
What to Build to Practice
CRUD API — Products or Todo items with EF Core + SQL Server/SQLite
Auth API — Register, login, return JWT token, protect routes
Blog API — Articles, categories, comments — connects to a real frontend
Final Thoughts
.NET Core is a mature, high-performance framework that powers everything from small startups to large banks. The key concepts that come up in every interview are Dependency Injection, EF Core, middleware, and the request/response pipeline. Understand those deeply and you'll be ahead of most candidates.


