ASP.NET Core  

Customer Support Ticket App — ASP.NET Core + Angular

Introduction

This article explains how to build a simple, production-minded Customer Support Ticket application using ASP.NET Core for the backend and Angular for the frontend. It focuses on practical implementation: source structure, models, APIs, authentication, Angular services and components, database design, and real-world best practices.

The guide is written in clear Indian English and is aimed at senior developers who want a copy-pasteable, well-structured reference.

High-Level Overview

The application supports:

  • User authentication (agents and customers) using JWT

  • Creating, updating, and closing support tickets

  • Ticket assignment and comments (threaded messages)

  • File attachments (handled via blob storage)

  • Role-based authorization (Agent, Admin, Customer)

  • Basic dashboard with filters (status, priority, assignee)

Components

  • ASP.NET Core Web API (controllers, services, EF Core)

  • Angular SPA (services, components, state management)

  • SQL Server for relational data

  • Optional: Azure Blob Storage or S3 for attachments

Architecture Diagram (ASCII)

[Angular SPA] <----HTTPS/JWT----> [ASP.NET Core Web API]
     |                                  |
     |                                  +---> [SQL Server (EF Core)]
     |                                  |
     +---> [File Uploads] --------------> [Blob Storage (Azure/AWS)]

Database Design (ERD)

Keep schema small but practical. Main tables:

  • Users(Id, UserName, Email, PasswordHash, Role, CreatedAt)

  • Tickets(Id, Title, Description, CustomerId, AssigneeId, Priority, Status, CreatedAt, UpdatedAt)

  • TicketComments(Id, TicketId, AuthorId, Content, CreatedAt)

  • Attachments(Id, TicketId, FileName, ContentType, BlobUrl, CreatedAt)

Relationships

Users (1) --- (M) Tickets (as CustomerId)
Users (1) --- (M) Tickets (as AssigneeId)
Tickets (1) --- (M) TicketComments
Tickets (1) --- (M) Attachments

Use FK constraints, indexes on Status, Priority, AssigneeId, and CreatedAt for efficient querying.

Backend: Project Structure

Create an ASP.NET Core Web API project named SupportBackend.

Suggested folders

  • Controllers

  • Models (EF entities and DTOs)

  • Services (business logic)

  • Repositories (data access - optional)

  • Hubs (for real-time notifications if needed)

  • Migrations

  • Helpers (JWT, file helpers)

Backend: Models (EF Core)

Ticket.cs

public class Ticket
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public Guid CustomerId { get; set; }
    public Guid? AssigneeId { get; set; }
    public TicketPriority Priority { get; set; }
    public TicketStatus Status { get; set; }
    public DateTimeOffset CreatedAt { get; set; }
    public DateTimeOffset UpdatedAt { get; set; }

    public List<TicketComment> Comments { get; set; } = new();
    public List<Attachment> Attachments { get; set; } = new();
}

TicketComment.cs

public class TicketComment
{
    public Guid Id { get; set; }
    public Guid TicketId { get; set; }
    public Guid AuthorId { get; set; }
    public string Content { get; set; }
    public DateTimeOffset CreatedAt { get; set; }
}

Attachment.cs

public class Attachment
{
    public Guid Id { get; set; }
    public Guid TicketId { get; set; }
    public string FileName { get; set; }
    public string ContentType { get; set; }
    public string BlobUrl { get; set; }
    public DateTimeOffset CreatedAt { get; set; }
}

Enums

public enum TicketPriority { Low, Medium, High, Critical }
public enum TicketStatus { Open, InProgress, Resolved, Closed }

Register these in SupportDbContext and add migrations.

Backend: DTOs and Mapping

Use DTOs to avoid over-posting and for versioning. Use AutoMapper or manual mapping.

TicketCreateDto

public class TicketCreateDto
{
    public string Title { get; set; }
    public string Description { get; set; }
    public TicketPriority Priority { get; set; }
}

TicketDto (returned to client)

public class TicketDto
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string CustomerName { get; set; }
    public string AssigneeName { get; set; }
    public TicketPriority Priority { get; set; }
    public TicketStatus Status { get; set; }
    public DateTimeOffset CreatedAt { get; set; }
}

Backend: Services and Repositories

Keep business logic in services. Example ITicketService:

public interface ITicketService
{
    Task<TicketDto> CreateTicketAsync(Guid customerId, TicketCreateDto dto);
    Task<PagedResult<TicketDto>> GetTicketsAsync(TicketQuery query);
    Task<TicketDto> GetTicketAsync(Guid ticketId);
    Task AddCommentAsync(Guid ticketId, Guid authorId, string content);
    Task AssignTicketAsync(Guid ticketId, Guid agentId);
    Task UpdateStatusAsync(Guid ticketId, TicketStatus status);
}

Implement TicketService using EF Core with transactions where necessary.

Best practice: Validate permissions in service layer (e.g., only agents can assign tickets).

Backend: Controllers (API endpoints)

Create TicketsController with endpoints:

  • POST /api/tickets — create ticket (Customer)

  • GET /api/tickets — list tickets with filters and pagination

  • GET /api/tickets/{id} — get ticket details

  • POST /api/tickets/{id}/comments — add comment

  • POST /api/tickets/{id}/assign — assign to agent (Agent/Admin)

  • POST /api/tickets/{id}/status — update status

  • POST /api/tickets/{id}/attachments — upload files

Use [Authorize] on controllers and [Authorize(Roles = "Agent,Admin")] for assignment endpoints.

Example: Create Ticket

[HttpPost]
public async Task<IActionResult> Create([FromBody] TicketCreateDto dto)
{
    var userId = User.GetUserId(); // extension to get claim
    var ticket = await _ticketService.CreateTicketAsync(userId, dto);
    return CreatedAtAction(nameof(Get), new { id = ticket.Id }, ticket);
}

Authentication and Authorization

Use ASP.NET Core Identity or a lightweight JWT solution. For many corporate apps, using Identity with EF Core is fine.

Steps

  1. Register Identity and JWT in Program.cs.

  2. Secure endpoints with [Authorize] and role policies.

  3. On login, return a JWT with sub as user id and role claims.

Important: Do not store plain passwords. Use secure hashing (Identity does this for you). Use short-lived JWTs and refresh tokens.

File Attachments

Files should be uploaded via controller to blob storage. Store metadata (BlobUrl) in Attachments table.

Controller snippet

[HttpPost("{id}/attachments")]
public async Task<IActionResult> Upload(Guid id, IFormFile file)
{
    if (file.Length == 0) return BadRequest();
    var blobUrl = await _blobService.UploadFileAsync(file);
    await _ticketService.AddAttachmentAsync(id, file.FileName, file.ContentType, blobUrl);
    return Ok();
}

Security: Scan content type, limit file size, and sanitize file names.

Optional: Real-time Notifications (SignalR)

Add SignalR hub to broadcast ticket events (new ticket, assigned, status changed, comment added). Agents can subscribe to events.

Hub events: TicketCreated, TicketAssigned, CommentAdded, TicketStatusChanged.

Why: Improves agent responsiveness and dashboard UX.

Frontend: Angular Project Structure

Create Angular app support-portal and organize:

  • src/app/core (auth, http interceptor, models)

  • src/app/tickets (services, components, ticket list, ticket detail)

  • src/app/shared (components like file uploader, pagination)

  • src/app/dashboard (agent dashboard)

Install libraries

npm install @auth0/angular-jwt
npm install @microsoft/signalr (optional for real-time)

Frontend: Authentication (JWT)

Implement AuthService to login and store token in localStorage. Use AuthInterceptor to attach the Bearer token to requests.

auth.service.ts

login(creds) { return this.http.post('/api/auth/login', creds).pipe(tap(res => localStorage.setItem('token', res.token))); }
logout() { localStorage.removeItem('token'); }
getToken() { return localStorage.getItem('token'); }

auth.interceptor.ts

intercept(req, next) {
  const token = this.auth.getToken();
  if (token) req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
  return next.handle(req);
}

Frontend: Ticket Service

ticket.service.ts handles API calls and maps responses to client models.

Key methods

  • createTicket(dto)

  • getTickets(filter)

  • getTicket(id)

  • addComment(ticketId, comment)

  • assignTicket(ticketId, agentId)

  • updateStatus(ticketId, status)

  • uploadAttachment(ticketId, file)

Use RxJS BehaviorSubject for current ticket and list updates so components can react in real time.

Frontend: Components

Create the following components:

  • ticket-list — server-side pagination, filters for status and priority.

  • ticket-detail — shows ticket, comments, attachments, and add-comment form.

  • ticket-create — form to create new tickets with validations.

  • agent-dashboard — shows unassigned tickets and quick actions.

Ticket-create form: Use reactive forms with validation rules for Title (required), Description (min length), Priority.

Ticket-detail: Keep comments in a scrollable panel and provide optimistic UI for posting comments (show locally then confirm server response).

File Upload Component

Use <input type="file"> and FormData to POST to /api/tickets/{id}/attachments.

upload function

const fd = new FormData();
fd.append('file', file);
this.http.post(`/api/tickets/${id}/attachments`, fd, { reportProgress: true, observe: 'events' })
  .subscribe(event => { /* handle progress and response */ });

Real-world Best Practices

  • Validation: Validate both client and server.

  • Pagination & Filters: Always use server-side pagination for ticket lists.

  • Transactions: Use transactions for ticket state changes that affect multiple tables.

  • Concurrency: Implement optimistic concurrency (RowVersion) to avoid lost updates.

  • Logging & Telemetry: Log user actions (not message content) and use Application Insights or ELK.

  • Rate limiting: Apply rate limits to APIs that can be abused (comment posting, file uploads).

  • Data retention: Add archival policies for old tickets.

  • Testing: Unit test services and integration test controllers with in-memory DB.

Security Considerations

  • Use HTTPS always.

  • Restrict CORS to trusted domains.

  • Use role-based authorization for sensitive endpoints.

  • Sanitize user input to prevent XSS.

  • Limit file upload size and scan files where possible.

Deployment Checklist

  • Use environment-specific configuration for DB connection, blob storage, and JWT keys.

  • Run EF migrations during deployment.

  • Use CI/CD pipelines to build, test, and deploy.

  • For scaling: run multiple API instances, use a load balancer, and share a common blob store and DB.

  • If using SignalR for notifications, choose Azure SignalR service or Redis backplane.

Monitoring and Alerts

Monitor:

  • API errors and failed requests

  • Average response time and queue length

  • Storage usage for attachments

  • Ticket creation rate and SLA breaches

Set alerts for increased error rates, high latency, and running out of storage.

Sample Sequence: Create Ticket and Notify Agent

Customer -> Angular -> POST /api/tickets
API -> Create ticket (DB) -> SignalR Hub broadcast TicketCreated to agents
Agent Dashboard (subscribed) receives event and refreshes list
Agent -> Assign ticket -> POST /api/tickets/{id}/assign -> API updates ticket -> Hub sends TicketAssigned

Testing Strategy

  • Unit tests for services and business logic

  • Controller tests using WebApplicationFactory and in-memory DB

  • E2E tests for Angular with Protractor or Cypress

  • Load test critical endpoints and file uploads

Example: Implementing CreateTicketAsync (Service)

public async Task<TicketDto> CreateTicketAsync(Guid customerId, TicketCreateDto dto)
{
    var ticket = new Ticket
    {
        Id = Guid.NewGuid(),
        Title = dto.Title,
        Description = dto.Description,
        CustomerId = customerId,
        Priority = dto.Priority,
        Status = TicketStatus.Open,
        CreatedAt = DateTimeOffset.UtcNow,
        UpdatedAt = DateTimeOffset.UtcNow
    };

    _db.Tickets.Add(ticket);
    await _db.SaveChangesAsync();

    var result = _mapper.Map<TicketDto>(ticket);

    // notify via SignalR
    await _notificationService.TicketCreated(result);

    return result;
}

Next Steps and Extensions

  • Implement SLA tracking and automated escalation rules

  • Add search with full-text indexing

  • Add multi-tenant support for SaaS scenarios

  • Provide audit logs and export features

Conclusion

This article provided a full, practical plan to build a Customer Support Ticket application with ASP.NET Core and Angular. It included database design, API surface, Angular structure, security, file handling, real-time notifications, and production best practices.

If you want, I can now:

  • generate a ready-to-run GitHub repository scaffold with backend and frontend,

  • add detailed EF Core migration scripts and seed data,

  • include full Angular component code with styles and unit tests,

  • provide PNG/SVG diagrams (flowchart, ERD, sequence) and upload them into the document.

Tell me which extension you would like next.