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

397 lines
8.9 KiB
Markdown

# Development Guide
## Quick Start Commands
### Build Solution
```bash
dotnet build PowderCoatingApp.sln
```
### Run Web Application
```bash
cd src/PowderCoating.Web
dotnet run
```
### Run API
```bash
cd src/PowderCoating.Api
dotnet run
```
### Watch Mode (Auto-reload)
```bash
cd src/PowderCoating.Web
dotnet watch run
```
## Entity Framework Core Commands
### Add Migration
```bash
cd src/PowderCoating.Web
dotnet ef migrations add MigrationName --project ../PowderCoating.Infrastructure
```
### Update Database
```bash
dotnet ef database update --project ../PowderCoating.Infrastructure
```
### Remove Last Migration
```bash
dotnet ef migrations remove --project ../PowderCoating.Infrastructure
```
### Generate SQL Script
```bash
dotnet ef migrations script --project ../PowderCoating.Infrastructure --output migration.sql
```
### View Migrations
```bash
dotnet ef migrations list --project ../PowderCoating.Infrastructure
```
## Project Management
### Add New NuGet Package
```bash
cd src/PowderCoating.Core
dotnet add package PackageName
```
### Restore Packages
```bash
dotnet restore
```
### Clean Solution
```bash
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:
```csharp
// 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:
```csharp
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:
```csharp
[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:
```csharp
// 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
```csharp
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)
```csharp
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
```csharp
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`:
```json
"Logging": {
"LogLevel": {
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
```
In `DbContext` configuration (development only):
```csharp
options.UseSqlServer(connectionString)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
```
### View Generated SQL
```csharp
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"
```bash
# 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
```bash
# Solution: Ensure you're using proper DI scoping and not storing DbContext
# Use IUnitOfWork which has proper lifetime management
```
**Issue**: Circular reference in JSON
```csharp
// Solution: Use DTOs or configure JSON serialization
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
```
## Testing
### Unit Test Example
```csharp
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
```bash
cd src/PowderCoating.Web
dotnet publish -c Release -o ./publish
```
### Publish API
```bash
cd src/PowderCoating.Api
dotnet publish -c Release -o ./publish
```
### Generate Database Script for Production
```bash
cd src/PowderCoating.Web
dotnet ef migrations script --project ../PowderCoating.Infrastructure --idempotent --output deploy.sql
```
## Resources
- [ASP.NET Core Documentation](https://docs.microsoft.com/aspnet/core)
- [Entity Framework Core Documentation](https://docs.microsoft.com/ef/core)
- [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
- [C# Coding Conventions](https://docs.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions)