For example, you can add a ToTitleCase method to the string class to convert a sentence into title case without altering the string class itself.
How to Create an Extension Method
Creating an extension method is straightforward. Here’s a step-by-step guide:
Define a Static Class
The class that holds your extension methods must be static.
Write a Static Method
The method itself must be static and take the type you want to extend as the first parameter, prefixed with the this keyword.
using Microsoft.EntityFrameworkCore;
using PetShopSimulation.DAL.Repositories.Ports;
using System.Linq.Expressions;
namespace PetShopSimulation.DAL.Repositories
{
public class PositionRepository : IRepository<Position>
{
private readonly SafeCamDbContext _context;
public PositionRepository(SafeCamDbContext context)
{
_context = context;
}
public DbSet<Position> Table => _context.Positions;
public async Task<Position?> GetByIdAsync(int id, params Expression<Func<Position, object>>[] includes)
{
IQueryable<Position> query = Table;
foreach (var include in includes)
{
query = query.Include(include);
}
return await query.FirstOrDefaultAsync(x => x.Id == id);
}
public IQueryable<Position> GetAll(params Expression<Func<Position, object>>[] includes)
{
IQueryable<Position> query = Table;
foreach (var include in includes)
{
query = query.Include(include);
}
return query;
}
public async Task<Position> AddAsync(Position entity)
{
await Table.AddAsync(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task UpdateAsync(Position entity)
{
Table.Update(entity);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(Position entity)
{
Table.Remove(entity);
await _context.SaveChangesAsync();
}
}
}
Best Practices for Writing Extensions
To make the most of extension methods, follow these guidelines:
Keep It Simple
Extension methods should perform a single, well-defined task. Avoid overloading them with too much logic.
Ensure Compatibility
Use extension methods for functionality that is universally applicable to the type. Avoid adding niche behavior that’s only relevant in specific contexts.
Handle Null Gracefully
Always consider null inputs to avoid runtime exceptions.
Name Methods Intuitively
Choose method names that clearly describe their purpose to improve code readability.
using System.Linq.Expressions;
namespace PetShopSimulation.BLL.Services
{
public class PositionService
{
private readonly IRepository<Position> _repository;
public PositionService(IRepository<Position> repository)
{
_repository = repository;
}
public async Task<IEnumerable<Position>> GetAllPositionsAsync()
{
return await _repository.GetAll().ToListAsync();
}
public async Task<Position?> GetPositionByIdAsync(int id)
{
return await _repository.GetByIdAsync(id);
}
public async Task AddPositionAsync(PositionCreateDto dto, IMapper mapper)
{
var position = mapper.Map<Position>(dto);
await _repository.AddAsync(position);
}
public async Task UpdatePositionAsync(PositionUpdateDto dto, IMapper mapper)
{
var position = mapper.Map<Position>(dto);
await _repository.UpdateAsync(position);
}
public async Task DeletePositionAsync(int id)
{
var position = await _repository.GetByIdAsync(id);
if (position == null)
{
throw new NotPositionException();
}
await _repository.DeleteAsync(position);
}
}
}
Why and When to Use Extension Methods
Extension methods are incredibly versatile and can be used in various scenarios:
Utility Functions: Create helper methods for common operations, such as string manipulations or date calculations.
Framework Integration: Add functionality to types from external libraries without modifying their source code.
Improved Readability: Make your code more expressive and easier to understand by encapsulating logic in extension methods.
using PetShopSimulation.BLL.Services;
namespace PetShopSimulation.Controllers
{
public class PositionController : Controller
{
private readonly PositionService _service;
private readonly IMapper _mapper;
public PositionController(PositionService service, IMapper mapper)
{
_service = service;
_mapper = mapper;
}
public async Task<IActionResult> Index()
{
var positions = await _service.GetAllPositionsAsync();
return View(positions);
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(PositionCreateDto dto)
{
if (!ModelState.IsValid) return View();
await _service.AddPositionAsync(dto, _mapper);
return RedirectToAction("Index");
}
[HttpGet]
public async Task<IActionResult> Update(int id)
{
var position = await _service.GetPositionByIdAsync(id);
if (position == null) throw new NotPositionException();
var positionDto = _mapper.Map<PositionUpdateDto>(position);
return View(positionDto);
}
[HttpPost]
public async Task<IActionResult> Update(PositionUpdateDto dto)
{
if (!ModelState.IsValid) return View(dto);
await _service.UpdatePositionAsync(dto, _mapper);
return RedirectToAction("Index");
}
[HttpPost]
public async Task<IActionResult> Delete(int id)
{
await _service.DeletePositionAsync(id);
return RedirectToAction("Index");
}
}
}
Top comments (0)