Files
PowderCoatingLogix/DEVELOPMENT.md
T
2026-04-23 21:38:24 -04:00

8.9 KiB

Development Guide

Quick Start Commands

Build Solution

dotnet build PowderCoatingApp.sln

Run Web Application

cd src/PowderCoating.Web
dotnet run

Run API

cd src/PowderCoating.Api
dotnet run

Watch Mode (Auto-reload)

cd src/PowderCoating.Web
dotnet watch run

Entity Framework Core Commands

Add Migration

cd src/PowderCoating.Web
dotnet ef migrations add MigrationName --project ../PowderCoating.Infrastructure

Update Database

dotnet ef database update --project ../PowderCoating.Infrastructure

Remove Last Migration

dotnet ef migrations remove --project ../PowderCoating.Infrastructure

Generate SQL Script

dotnet ef migrations script --project ../PowderCoating.Infrastructure --output migration.sql

View Migrations

dotnet ef migrations list --project ../PowderCoating.Infrastructure

Project Management

Add New NuGet Package

cd src/PowderCoating.Core
dotnet add package PackageName

Restore Packages

dotnet restore

Clean Solution

dotnet clean

Creating New Components

Add New Entity

  1. Create entity class in PowderCoating.Core/Entities/
  2. Inherit from BaseEntity
  3. Add DbSet to ApplicationDbContext
  4. Add repository to IUnitOfWork and UnitOfWork
  5. Create migration
  6. Update database

Example:

// 1. Create entity
public class NewEntity : BaseEntity
{
    public string Name { get; set; } = string.Empty;
    // ... properties
}

// 2. Add to DbContext
public DbSet<NewEntity> NewEntities { get; set; }

// 3. Add to IUnitOfWork
IRepository<NewEntity> NewEntities { get; }

// 4. Add to UnitOfWork
private IRepository<NewEntity>? _newEntities;
public IRepository<NewEntity> NewEntities => 
    _newEntities ??= new Repository<NewEntity>(_context);

// 5. Add migration
dotnet ef migrations add AddNewEntity --project ../PowderCoating.Infrastructure

// 6. Update database
dotnet ef database update --project ../PowderCoating.Infrastructure

Add New Controller (Web)

  1. Create in PowderCoating.Web/Controllers/
  2. Inherit from Controller
  3. Inject IUnitOfWork and IMapper
  4. Create corresponding views in Views/[ControllerName]/

Example:

public class NewController : Controller
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IMapper _mapper;

    public NewController(IUnitOfWork unitOfWork, IMapper mapper)
    {
        _unitOfWork = unitOfWork;
        _mapper = mapper;
    }

    public async Task<IActionResult> Index()
    {
        var items = await _unitOfWork.NewEntities.GetAllAsync();
        return View(items);
    }
}

Add New API Controller

  1. Create in PowderCoating.Api/Controllers/
  2. Add [ApiController] and [Route("api/[controller]")] attributes
  3. Inject dependencies
  4. Return appropriate ActionResult<T> types

Example:

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class NewApiController : ControllerBase
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IMapper _mapper;

    public NewApiController(IUnitOfWork unitOfWork, IMapper mapper)
    {
        _unitOfWork = unitOfWork;
        _mapper = mapper;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<NewDto>>> GetAll()
    {
        var items = await _unitOfWork.NewEntities.GetAllAsync();
        var dtos = _mapper.Map<IEnumerable<NewDto>>(items);
        return Ok(dtos);
    }
}

Add New DTO

  1. Create in PowderCoating.Application/DTOs/[Module]/
  2. Create AutoMapper profile

Example:

// DTO
public class NewDto
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

// AutoMapper Profile
public class NewProfile : Profile
{
    public NewProfile()
    {
        CreateMap<NewEntity, NewDto>();
        CreateMap<CreateNewDto, NewEntity>();
    }
}

Best Practices

Code Style

  • Follow C# coding conventions
  • Use meaningful variable and method names
  • Add XML documentation for public APIs
  • Keep methods focused (Single Responsibility)
  • Use async/await for I/O operations

Entity Framework

  • Use async methods (ToListAsync, FirstOrDefaultAsync, etc.)
  • Include related entities when needed (Include, ThenInclude)
  • Use .AsNoTracking() for read-only queries
  • Handle concurrency conflicts
  • Use transactions for multi-step operations

API Development

  • Use appropriate HTTP status codes
  • Validate input with FluentValidation
  • Use DTOs, never expose entities directly
  • Document endpoints with XML comments for Swagger
  • Implement proper error handling

Security

  • Always validate user input
  • Use parameterized queries (EF Core does this)
  • Implement authorization on controllers/actions
  • Don't expose sensitive data in responses
  • Use HTTPS in production
  • Store secrets in user secrets or Azure Key Vault

Performance

  • Use pagination for large datasets
  • Implement caching where appropriate
  • Use projection (Select) to retrieve only needed fields
  • Profile and optimize slow queries
  • Use async for I/O-bound operations

Common Patterns

Service Pattern

public interface IJobService
{
    Task<JobDto> GetByIdAsync(int id);
    Task<IEnumerable<JobDto>> GetAllAsync();
    Task<JobDto> CreateAsync(CreateJobDto dto);
    Task UpdateAsync(UpdateJobDto dto);
    Task DeleteAsync(int id);
}

public class JobService : IJobService
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IMapper _mapper;

    public JobService(IUnitOfWork unitOfWork, IMapper mapper)
    {
        _unitOfWork = unitOfWork;
        _mapper = mapper;
    }

    // Implementation...
}

Repository Pattern (Already Implemented)

var customers = await _unitOfWork.Customers.FindAsync(c => c.IsActive);
var customer = await _unitOfWork.Customers.GetByIdAsync(id);
await _unitOfWork.Customers.AddAsync(newCustomer);
await _unitOfWork.SaveChangesAsync();

Unit of Work with Transaction

try
{
    await _unitOfWork.BeginTransactionAsync();
    
    // Multiple operations
    await _unitOfWork.Jobs.AddAsync(job);
    await _unitOfWork.JobItems.AddRangeAsync(items);
    
    await _unitOfWork.CommitTransactionAsync();
}
catch
{
    await _unitOfWork.RollbackTransactionAsync();
    throw;
}

Debugging Tips

Enable Sensitive Data Logging

In appsettings.Development.json:

"Logging": {
  "LogLevel": {
    "Microsoft.EntityFrameworkCore.Database.Command": "Information"
  }
}

In DbContext configuration (development only):

options.UseSqlServer(connectionString)
    .EnableSensitiveDataLogging()
    .EnableDetailedErrors();

View Generated SQL

var query = _context.Jobs.Where(j => j.Status == JobStatus.Pending);
var sql = query.ToQueryString();
Console.WriteLine(sql);

Common Issues

Issue: Migration fails with "object already exists"

# Solution: Remove migration and try again
dotnet ef migrations remove --project ../PowderCoating.Infrastructure
dotnet ef migrations add NewMigration --project ../PowderCoating.Infrastructure

Issue: DbContext is disposed

# Solution: Ensure you're using proper DI scoping and not storing DbContext
# Use IUnitOfWork which has proper lifetime management

Issue: Circular reference in JSON

// Solution: Use DTOs or configure JSON serialization
services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
    });

Testing

Unit Test Example

public class CustomerServiceTests
{
    [Fact]
    public async Task GetByIdAsync_ReturnsCustomer_WhenExists()
    {
        // Arrange
        var mockUnitOfWork = new Mock<IUnitOfWork>();
        var customer = new Customer { Id = 1, CompanyName = "Test" };
        
        mockUnitOfWork
            .Setup(x => x.Customers.GetByIdAsync(1))
            .ReturnsAsync(customer);

        var service = new CustomerService(mockUnitOfWork.Object, mapper);

        // Act
        var result = await service.GetByIdAsync(1);

        // Assert
        Assert.NotNull(result);
        Assert.Equal("Test", result.CompanyName);
    }
}

Deployment

Publish Web Application

cd src/PowderCoating.Web
dotnet publish -c Release -o ./publish

Publish API

cd src/PowderCoating.Api
dotnet publish -c Release -o ./publish

Generate Database Script for Production

cd src/PowderCoating.Web
dotnet ef migrations script --project ../PowderCoating.Infrastructure --idempotent --output deploy.sql

Resources