8.9 KiB
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
- Create entity class in
PowderCoating.Core/Entities/ - Inherit from
BaseEntity - Add DbSet to
ApplicationDbContext - Add repository to
IUnitOfWorkandUnitOfWork - Create migration
- 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)
- Create in
PowderCoating.Web/Controllers/ - Inherit from
Controller - Inject
IUnitOfWorkandIMapper - 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
- Create in
PowderCoating.Api/Controllers/ - Add
[ApiController]and[Route("api/[controller]")]attributes - Inject dependencies
- 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
- Create in
PowderCoating.Application/DTOs/[Module]/ - 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