Initial commit
This commit is contained in:
+396
@@ -0,0 +1,396 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user