Introduction
Building a full-stack application may feel challenging for beginners, especially when you need to connect different technologies such as Angular, ASP.NET Core, and SQL Server. However, once you understand the flow and how each layer communicates, full-stack development becomes simple and enjoyable.
In this guide, you will learn how to build a complete CRUD (Create, Read, Update, Delete) application using:
Angular for the frontend
ASP.NET Core Web API for the backend
Entity Framework Core (EF Core) for database access
SQL Server for data storage
By the end of this article, you will have a fully functional full-stack project where Angular communicates with ASP.NET Core, and ASP.NET Core interacts with SQL Server through EF Core.
Understanding the Full-Stack Flow
Before writing any code, it is important to understand the flow of data in a full-stack application:
Angular (Frontend)
↓ HTTP Calls (GET, POST, PUT, DELETE)
ASP.NET Core Web API (Backend)
↓ EF Core (ORM)
SQL Server (Database)
Angular displays UI, captures user data, and sends requests.
ASP.NET Core API exposes endpoints for CRUD operations.
EF Core performs database operations.
SQL Server stores the actual data.
This architecture ensures proper separation of concerns and makes your application scalable, maintainable, and professional.
Part 1: Setting Up the Backend with ASP.NET Core and EF Core
Step 1: Create a New ASP.NET Core Web API Project
Open terminal:
dotnet new webapi -n AngularAspNetEfCoreDemo
cd AngularAspNetEfCoreDemo
This creates the backend project.
Step 2: Install EF Core and SQL Server Packages
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
These packages allow the application to work with SQL Server through EF Core.
Step 3: Create a Model (Database Table Structure)
Create folder Models > Product.cs:
namespace AngularAspNetEfCoreDemo.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
}
This model represents a product in the system.
Step 4: Create a DbContext Class
Create folder Data > AppDbContext.cs:
using Microsoft.EntityFrameworkCore;
using AngularAspNetEfCoreDemo.Models;
namespace AngularAspNetEfCoreDemo.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}
public DbSet<Product> Products { get; set; }
}
}
DbSet<Product> represents the Products table in SQL Server.
Step 5: Add Connection String
Open appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=ProductDB;Trusted_Connection=True;"
}
}
EF Core will use this connection string to connect to SQL Server.
Step 6: Register DbContext in Program.cs
Open Program.cs and update:
using Microsoft.EntityFrameworkCore;
using AngularAspNetEfCoreDemo.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
app.MapControllers();
app.Run();
Now the backend knows how to connect to SQL Server.
Step 7: Create Database Using EF Core Migrations
dotnet ef migrations add InitialCreate
dotnet ef database update
This creates:
Database: ProductDB
Table: Products
Step 8: Create Products API Controller
Create file: Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AngularAspNetEfCoreDemo.Data;
using AngularAspNetEfCoreDemo.Models;
namespace AngularAspNetEfCoreDemo.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly AppDbContext _context;
public ProductsController(AppDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<IEnumerable<Product>> GetProducts()
{
return await _context.Products.ToListAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetProduct(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null) return NotFound();
return product;
}
[HttpPost]
public async Task<ActionResult<Product>> AddProduct(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateProduct(int id, Product product)
{
if (id != product.Id) return BadRequest();
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null) return NotFound();
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return NoContent();
}
}
}
Your backend is now fully functional.
Part 2: Creating the Angular Frontend
Step 1: Create Angular Project
Open terminal:
ng new product-app
cd product-app
ng serve -o
This creates and launches the Angular application.
Step 2: Create a Service for API Calls
Create:
src/app/services/product.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
export interface Product {
id: number;
name: string;
price: number;
quantity: number;
}
@Injectable({
providedIn: 'root'
})
export class ProductService {
private apiUrl = 'https://localhost:5001/api/products';
constructor(private http: HttpClient) {}
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.apiUrl);
}
getProduct(id: number): Observable<Product> {
return this.http.get<Product>(`${this.apiUrl}/${id}`);
}
addProduct(product: Product): Observable<Product> {
return this.http.post<Product>(this.apiUrl, product);
}
updateProduct(product: Product): Observable<void> {
return this.http.put<void>(`${this.apiUrl}/${product.id}`, product);
}
deleteProduct(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}
This service communicates with the ASP.NET Core backend.
Step 3: Add HttpClientModule
Open app.module.ts:
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [],
imports: [
BrowserModule,
HttpClientModule,
FormsModule
],
bootstrap: [AppComponent]
})
export class AppModule { }
This allows Angular to make HTTP requests.
Step 4: Create Product Components
ng generate component components/product-list
ng generate component components/product-form
Step 5: Product List Component
product-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Product, ProductService } from '../../services/product.service';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html'
})
export class ProductListComponent implements OnInit {
products: Product[] = [];
constructor(private service: ProductService) {}
ngOnInit(): void {
this.loadProducts();
}
loadProducts() {
this.service.getProducts().subscribe(res => {
this.products = res;
});
}
deleteProduct(id: number) {
this.service.deleteProduct(id).subscribe(() => {
this.loadProducts();
});
}
}
product-list.component.html
<h2>Product List</h2>
<table border="1" width="100%">
<tr>
<th>Id</th>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Actions</th>
</tr>
<tr *ngFor="let p of products">
<td>{{ p.id }}</td>
<td>{{ p.name }}</td>
<td>{{ p.price }}</td>
<td>{{ p.quantity }}</td>
<td>
<button (click)="deleteProduct(p.id)">Delete</button>
</td>
</tr>
</table>
Step 6: Product Form Component
product-form.component.ts
import { Component } from '@angular/core';
import { ProductService, Product } from '../../services/product.service';
@Component({
selector: 'app-product-form',
templateUrl: './product-form.component.html'
})
export class ProductFormComponent {
product: Product = { id: 0, name: '', price: 0, quantity: 0 };
constructor(private service: ProductService) {}
addProduct() {
this.service.addProduct(this.product).subscribe(() => {
alert('Product added successfully');
this.product = { id: 0, name: '', price: 0, quantity: 0 };
});
}
}
product-form.component.html
<h2>Add Product</h2>
<form (submit)="addProduct()">
<label>Name</label>
<input [(ngModel)]="product.name" name="name">
<label>Price</label>
<input [(ngModel)]="product.price" name="price">
<label>Quantity</label>
<input [(ngModel)]="product.quantity" name="quantity">
<button type="submit">Add</button>
</form>
Step 7: Add Components to App Component
Open app.component.html:
<h1>Product Management System</h1>
<app-product-form></app-product-form>
<hr>
<app-product-list></app-product-list>
Understanding the Integration
Now your full-stack system works like this:
1. Angular UI sends requests
GET /api/products
POST /api/products
2. ASP.NET Core Web API receives and processes requests
3. EF Core interacts with SQL Server
4. SQL Server stores all product information
5. Data returns to Angular
This completes the full cycle of a full-stack CRUD application.
Conclusion
Building a full-stack application using Angular + ASP.NET Core + EF Core is easier once you understand how each part works. You learned:
How to build an ASP.NET Core Web API with CRUD operations
How to use EF Core to interact with SQL Server
How to create Angular components and services
How to connect Angular to the backend using HTTP
How the entire full-stack flow works
This architecture is used widely in enterprise applications because it is clean, modular, maintainable, fast, and scalable.