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.
When Not to Use Extension Methods
Excessive Logic: Avoid adding complex logic or functionality that would be better suited to a dedicated class or method.
Code Clarity: Overuse of extension methods can make code harder to follow, especially for developers unfamiliar with the custom methods.
Dependency on Internal Details: Avoid writing extension methods that rely on the internal implementation details of a class, as they can break with updates to the class.
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
namespace PetShopSimulation.BLL.Services
{
public class AccountService
{
private readonly UserManager<AppUser> _userManager;
private readonly SignInManager<AppUser> _signInManager;
private readonly RoleManager<IdentityRole> _roleManager;
private readonly IMapper _mapper;
public AccountService(UserManager<AppUser> userManager, SignInManager<AppUser> signInManager, RoleManager<IdentityRole> roleManager, IMapper mapper)
{
_userManager = userManager;
_signInManager = signInManager;
_roleManager = roleManager;
_mapper = mapper;
}
public async Task<IdentityResult> RegisterUserAsync(RegisterDto dto)
{
var user = _mapper.Map<AppUser>(dto);
return await _userManager.CreateAsync(user, dto.Password);
}
public async Task<SignInResult> LoginUserAsync(LoginDto dto)
{
AppUser user = dto.UsernameOrEmail.Contains("@")
? await _userManager.FindByEmailAsync(dto.UsernameOrEmail)
: await _userManager.FindByNameAsync(dto.UsernameOrEmail);
if (user == null) return SignInResult.Failed;
return await _signInManager.CheckPasswordSignInAsync(user, dto.Password, true);
}
public async Task SignInUserAsync(AppUser user, bool rememberMe)
{
await _signInManager.SignInAsync(user, rememberMe);
}
public async Task SignOutUserAsync()
{
await _signInManager.SignOutAsync();
}
public async Task CreateRolesAsync()
{
foreach (var item in Enum.GetValues(typeof(UserRole)))
{
if (!await _roleManager.RoleExistsAsync(item.ToString()))
{
await _roleManager.CreateAsync(new IdentityRole
{
Name = item.ToString()
});
}
}
}
}
}
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. Include checks to ensure that the method behaves predictably when invoked on null references.
Avoid Overuse
Don’t overuse extension methods to add trivial functionality. Reserve them for methods that add significant value or improve readability.
Name Methods Intuitively
Choose method names that clearly describe their purpose to improve code readability.
using PetShopSimulation.BLL.Services;
namespace PetShopSimulation.Controllers
{
public class AccountController : Controller
{
private readonly AccountService _accountService;
private readonly IMapper _mapper;
public AccountController(AccountService accountService, IMapper mapper)
{
_accountService = accountService;
_mapper = mapper;
}
public IActionResult Register()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Register(RegisterDto dto)
{
if (!ModelState.IsValid) return View();
var result = await _accountService.RegisterUserAsync(dto);
if (!result.Succeeded)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
return View();
}
return RedirectToAction("Login", "Account");
}
public IActionResult Login()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(LoginDto dto, string? returnUrl = null)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("", "Invalid login details.");
return View();
}
var result = await _accountService.LoginUserAsync(dto);
if (!result.Succeeded)
{
ModelState.AddModelError("", "Invalid username or password.");
return View();
}
var user = await _accountService.FindUserByUsernameOrEmailAsync(dto.UsernameOrEmail);
await _accountService.SignInUserAsync(user, dto.Remember);
return returnUrl != null ? Redirect(returnUrl) : RedirectToAction("Index", "Home");
}
public async Task<IActionResult> Logout()
{
await _accountService.SignOutUserAsync();
return RedirectToAction("Index", "Home");
}
public async Task<IActionResult> CreateRole()
{
await _accountService.CreateRolesAsync();
return Ok();
}
}
}
Top comments (0)