Introduction
In this post we will learn about Entity Framework core implementation in .Net Core API. The .Net Core API application has been explained in the below post.
Before we start please go through the above article to understand Entity Framework core. We are building an applicaiton on top of this.
About Repository Pattern
Repository Pattern is abstract layer to persistence layer and reduces the complexity of the code. An application should not depend on persistence implementation directly. It helps to change the Db layer implementation without direct impact to the application, and we can easily add mock repository which will help the unit testing.
Step 1
Create a “Repositories” folder and create “IEmployeeRepository.cs” and “EmployeeRepository.cs” class files.
Add the below methods in “IEmployeeRepository.cs” and “EmployeeRepository.cs” class files.
- public interface IEmployeeRepository
- {
- IEnumerable<Employee> GetAllEmployee();
- Employee GetEmployee(int employeeId, bool isWithAddress);
- IEnumerable<Address> GetAddress(int employeeId);
- Address GetEmployeeAddress(int employeeId, int addressId);
- }
Step 2
Implement “IEmployeeRepository” interface in “EmployeeRepository” class.
- public class EmployeeRepository : IEmployeeRepository
- {
- public IEnumerable<Address> GetAddress(int employeeId)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerable<Employee> GetAllEmployee()
- {
- throw new NotImplementedException();
- }
-
- public Employee GetEmployee(int employeeId)
- {
- throw new NotImplementedException();
- }
-
- public Address GetEmployeeAddress(int employeeId, int addressId)
- {
- throw new NotImplementedException();
- }
- }
Inject the "Dbcontext" class in “EmployeeRepository” class.
- private readonly EmployeeDbContext _context;
- public EmployeeRepository(EmployeeDbContext context)
- {
- _context = context;
- }
Step 3
We will implement “IEmployeeRepository” interface methods that will pull data from "EmployeeDbContext".
- public class EmployeeRepository : IEmployeeRepository
- {
- private readonly EmployeeDbContext _context;
- public EmployeeRepository(EmployeeDbContext context)
- {
- _context = context;
- }
-
- public IEnumerable<Employee> GetAllEmployee()
- {
- return _context.Employees.OrderBy(e => e.EmployeeName).ToList();
- }
-
- public Employee GetEmployee(int employeeId, bool isWithAddress)
- {
- if (isWithAddress)
- {
- return _context.Employees.Include(e => e.EmployeeAddress)
- .Where(a => a.Id == employeeId).FirstOrDefault();
- }
- return _context.Employees.Where(e => e.Id==employeeId).FirstOrDefault();
- }
-
- public Address GetEmployeeAddress(int employeeId, int addressId)
- {
- return _context.Addresses.Where(a => a.Id == employeeId
- && a.EmployeeId == employeeId).FirstOrDefault();
- }
-
- public IEnumerable<Address> GetAddress(int employeeId)
- {
- return _context.Addresses.Where(a => a.Id == employeeId).ToList();
- }
- }
Also register the "EmployeeRepository" in "Startup.cs" -> ConfigureServices method.
- services.AddScoped<IEmployeeRepository, EmployeeRepository>();
Step 4
We will inject the "EmployeeRepository" in "EmployeeController".
- private readonly ILogger<EmployeeController> _logger;
- private readonly IEmployeeRepository _employeeRepository;
- public EmployeeController(ILogger<EmployeeController> logger, IEmployeeRepository employeeRepository)
- {
- _logger = logger;
- _employeeRepository = employeeRepository;
- }
We can call the "GetAllEmployee" repository method and map repository data to "EmployeeModel" manually.
- public IActionResult GetAllEmployees()
- {
- var employeeData = _employeeRepository.GetAllEmployee();
- var result = new List<EmployeeModel>();
- foreach (var employee in employeeData)
- {
- result.Add(new EmployeeModel
- {
- EmployeeId=employee.Id,
- EmployeeName=employee.EmployeeName,
- Skill=employee.Skill,
- Email=employee.Email
- });
- }
- if (result == null)
- {
- return NotFound();
- }
- return Ok(result);
- }
Step 5
We will run the application and check whether we will able to get the records from the DB or not.
We got the 3data for the 3 employees from the DB as expected.
Step 6
We will implement the "GetEmployee" method with "employeeId" input. We can notice that "employeeAddress" field was empty array.
If we pass "IsWithAddress" parameter as true, we will get employee details with Address.
Step 7
Similary we will implement AddressController by adding the "EmployeeRepository" and invoking the Address methods.
- [ApiController]
- [Route("api/employee/{employeeId}/address")]
- public class AddressController : ControllerBase
- {
-
- private readonly ILogger<EmployeeController> _logger;
- private readonly IEmployeeRepository _employeeRepository;
- public AddressController(ILogger<EmployeeController> logger, IEmployeeRepository employeeRepository)
- {
- _logger = logger;
- _employeeRepository = employeeRepository;
- }
-
-
- [HttpGet]
- public IActionResult GetAllAddress(int employeeId)
- {
- if (!_employeeRepository.IsEmployeeAvailable(employeeId))
- {
- return NotFound();
- }
- var addressData = _employeeRepository.GetAddress(employeeId);
- var result = new List<AddressModel>();
-
- foreach (var address in addressData)
- {
- result.Add(new AddressModel
- {
- Id = address.Id,
- AddressDetail = address.AddressDetail
- });
- }
- if (result == null)
- {
- return NotFound();
- }
- return Ok(result);
- }
-
- [HttpGet("{id}", Name = "GetEmployeeAddress")]
- public IActionResult GetEmployeeAddress(int employeeId, int id)
- {
- if (!_employeeRepository.IsEmployeeAvailable(employeeId))
- {
- return NotFound();
- }
- var address = _employeeRepository.GetEmployeeAddress(employeeId, id);
-
- if (address == null)
- {
- return NotFound();
- }
- var employeeAddress = new AddressModel()
- {
- Id = address.Id,
- AddressDetail = address.AddressDetail
- };
- return Ok(employeeAddress);
- }
- }
Once we run the below endpoint, we are able to access the address data.
We can also get the employee address.
Step 8
We manually mapped our entities and models, instead we will add "Automapper" package. "Automapper" is a convention mapped object mapper which helps to map DB entities and model (DTO) class properties.
Install "AutoMapper" and "AutoMapper.Extensions.Microsoft.DependencyInjection" packages from NuGet.
Register "AutoMapper" services in "Services.cs" -> Configureservice method.
- services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
Create Mapper files
Create a "Mappers" folder and "EmployeeMapper" class file, which will inherit from Profile base class (Automapper library).
- public class EmployeeMapper: Profile
- {
- public EmployeeMapper()
- {
- CreateMap<Employee, EmployeeModel>();
- }
- }
Inject the "AutoMapper" -> "IMapper" in "EmployeeController" constructor.
- private IMapper _mapper;
-
- public EmployeeController(ILogger<EmployeeController> logger, IEmployeeRepository employeeRepository, IMapper mapper)
- {
- _logger = logger;
- _employeeRepository = employeeRepository;
- _mapper = mapper;
- }
Change all the manual mapping code to Automapper mapping using "Map" method.
- _mapper.Map<IEnumerable<EmployeeModel>>(employeeData)
After using Automapper, make sure all the code is working fine.
Step 9
Added the below code for your reference.
Progaram.cs
- public static void Main(string[] args)
- {
- var host = CreateHostBuilder(args).Build();
- using (var scope = host.Services.CreateScope())
- {
- try
- {
- var context = scope.ServiceProvider.GetService<EmployeeDbContext>();
- context.Database.EnsureDeleted();
- context.Database.Migrate();
- }
- catch (Exception)
- {
- throw;
- }
- }
- host.Run();
- }
Startup.cs
- public class Startup
- {
- private readonly IConfiguration _configuartion;
-
- public Startup(IConfiguration configuartion)
- {
- _configuartion = configuartion;
- }
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc();
- services.AddScoped<IEmployeeRepository, EmployeeRepository>();
- services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
- services.AddDbContext<EmployeeDbContext>(o=>
- {
- o.UseSqlServer(_configuartion["connectionStrings:EmployeeDbConnectionString"]);
- });
- }
-
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
- }
- }
IEmployeeRepository.cs
- public interface IEmployeeRepository
- {
- IEnumerable<Employee> GetAllEmployee();
- Employee GetEmployee(int employeeId, bool isWithAddress);
- IEnumerable<Address> GetAddress(int employeeId);
- Address GetEmployeeAddress(int employeeId, int addressId);
- bool IsEmployeeAvailable(int employeeId);
- }
EmployeeRepository.cs
- public class EmployeeRepository : IEmployeeRepository
- {
- private readonly EmployeeDbContext _context;
- public EmployeeRepository(EmployeeDbContext context)
- {
- _context = context;
- }
- public IEnumerable<Employee> GetAllEmployee()
- {
- return _context.Employees.OrderBy(e => e.EmployeeName).ToList();
- }
- public Employee GetEmployee(int employeeId, bool isWithAddress=false)
- {
- if (isWithAddress)
- {
- return _context.Employees.Include(e => e.EmployeeAddress)
- .Where(a => a.Id == employeeId).FirstOrDefault();
- }
- return _context.Employees.Where(e => e.Id==employeeId).FirstOrDefault();
- }
- public Address GetEmployeeAddress(int employeeId, int addressId)
- {
- return _context.Addresses.Where(a => a.Id == addressId
- && a.EmployeeId == employeeId).FirstOrDefault();
- }
- public IEnumerable<Address> GetAddress(int employeeId)
- {
- return _context.Addresses.Where(a => a.EmployeeId == employeeId).ToList();
- }
- public bool IsEmployeeAvailable(int employeeId)
- {
- return _context.Employees.Any(e => e.Id == employeeId);
- }
- }
EmployeeModel and AddressModel
- public class EmployeeModel
- {
- public int EmployeeId { get; set; }
- public string EmployeeName { get; set; }
- public string Skill { get; set; }
- public string Email { get; set; }
- public ICollection<AddressModel> EmployeeAddress { get; set; }
- = new List<AddressModel>();
- }
-
- public class AddressModel
- {
- public int Id { get; set; }
- public string AddressDetail { get; set; }
- }
Employee and Address Entities(DTO's)
- public class Employee
- {
- [Key]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; set; }
- public string EmployeeName { get; set; }
- public string Skill { get; set; }
- public string Email { get; set; }
-
- public ICollection<Address> EmployeeAddress { get; set; }
- = new List<Address>();
- }
-
- public class Address
- {
- [Key]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; set; }
- public string AddressDetail { get; set; }
- [ForeignKey("EmployeeId")]
- public Employee Employee { get; set; }
-
- public int EmployeeId { get; set; }
- }
EmployeeMapper.cs
- public class EmployeeMapper: Profile
- {
- public EmployeeMapper()
- {
- CreateMap<Employee, EmployeeModel>();
- CreateMap<Address, AddressModel>();
- }
- }
EmployeeController.cs
- [ApiController]
- [Route("api/[controller]")]
- public class EmployeeController : ControllerBase
- {
- private readonly ILogger<EmployeeController> _logger;
- private readonly IEmployeeRepository _employeeRepository;
- private IMapper _mapper;
- public EmployeeController(ILogger<EmployeeController> logger, IEmployeeRepository employeeRepository, IMapper mapper)
- {
- _logger = logger;
- _employeeRepository = employeeRepository;
- _mapper = mapper;
- }
-
- [HttpGet]
- public IActionResult GetAllEmployees()
- {
- var employeeData = _employeeRepository.GetAllEmployee();
- if (employeeData == null)
- {
- return NotFound();
- }
- return Ok(_mapper.Map<IEnumerable<EmployeeModel>>(employeeData));
- }
-
- [HttpGet("{id}")]
- public IActionResult GetEmployee(int id, bool isWithAddress = false)
- {
- var employee = _employeeRepository.GetEmployee(id, isWithAddress);
- if (employee == null)
- {
- return NotFound();
- }
- if (isWithAddress)
- {
- return Ok(_mapper.Map<EmployeeModel>(employee));
- }
- return Ok(_mapper.Map<EmployeeModel>(employee));
- }
- }
AddressController.cs
- [ApiController]
- [Route("api/employee/{employeeId}/address")]
- public class AddressController : ControllerBase
- {
- private readonly ILogger<EmployeeController> _logger;
- private readonly IEmployeeRepository _employeeRepository;
- private IMapper _mapper;
- public AddressController(ILogger<EmployeeController> logger, IEmployeeRepository employeeRepository, IMapper mapper)
- {
- _logger = logger;
- _employeeRepository = employeeRepository;
- _mapper = mapper;
- }
-
- [HttpGet]
- public IActionResult GetAllAddress(int employeeId)
- {
- if (!_employeeRepository.IsEmployeeAvailable(employeeId))
- {
- return NotFound();
- }
- var addressData = _employeeRepository.GetAddress(employeeId);
- if (addressData == null)
- {
- return NotFound();
- }
- return Ok(_mapper.Map<IEnumerable<Address>>(addressData));
- }
-
- [HttpGet("{id}", Name = "GetEmployeeAddress")]
- public IActionResult GetEmployeeAddress(int employeeId, int id)
- {
- if (!_employeeRepository.IsEmployeeAvailable(employeeId))
- {
- return NotFound();
- }
- var address = _employeeRepository.GetEmployeeAddress(employeeId, id);
- if (address == null)
- {
- return NotFound();
- }
- return Ok(_mapper.Map<Address>(address));
- }
- }
Summary
In this post we learned about Entity Framework core implementation in .Net Core API and complex entity - DTO mapping through AutoMapper.