I have implemented a .net core web api with Student and Department entities. One to One relationship is implemented between these entities where departmentid is foreignkey for student table. How can I implement api end point in DepartmentController to fetch all the departments with student included in it?
DepartmentController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Students.Models;
namespace Students.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DepartmentController : ControllerBase
{
private readonly StudentContext _context;
public DepartmentController(StudentContext context)
{
_context = context;
}
// GET: api/Department
[HttpGet]
public async Task<ActionResult<IEnumerable<Department>>> GetDepartments()
{
return await _context.Departments.ToListAsync();
}
// GET: api/Department/5
[HttpGet("{id}")]
public async Task<ActionResult<Department>> GetDepartment(int id)
{
var department = await _context.Departments.FindAsync(id);
if (department == null)
{
return NotFound();
}
return department;
}
// PUT: api/Department/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task<IActionResult> PutDepartment(int id, Department department)
{
if (id != department.Id)
{
return BadRequest();
}
_context.Entry(department).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!DepartmentExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return Ok();
}
// POST: api/Department
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task<ActionResult<Department>> PostDepartment(Department department)
{
_context.Departments.Add(department);
await _context.SaveChangesAsync();
return CreatedAtAction("GetDepartment", new { id = department.Id }, department);
}
// DELETE: api/Department/5
[HttpDelete("{id}")]
public async Task<ActionResult<Department>> DeleteDepartment(int id)
{
var department = await _context.Departments.FindAsync(id);
if (department == null)
{
return NotFound();
}
_context.Departments.Remove(department);
await _context.SaveChangesAsync();
return department;
}
private bool DepartmentExists(int id)
{
return _context.Departments.Any(e => e.Id == id);
}
}
}
Department.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Students.Models
{
public class Department
{
[Key]
public int Id { get; set; }
[Required]
[Column(TypeName = "varchar(20)")]
public string Dep { get; set; }
}
}
Student.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Students.Models
{
public class Student
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int SId { get; set; }
[Required]
[Column(TypeName ="varchar(50)")]
public string Name { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
}
StudentContext.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace Students.Models
{
public class StudentContext:DbContext
{
public StudentContext(DbContextOptions<StudentContext> options) : base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Department> Departments { get; set; }
}
}
StudentController.cs
Please refer GetStudents()
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Students.Models;
namespace Students.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentsController : ControllerBase
{
private readonly StudentContext _context;
public StudentsController(StudentContext context)
{
_context = context;
}
// GET: api/Students
[HttpGet]
public async Task<ActionResult<IEnumerable<Student>>> GetStudents()
{
return await _context.Students.Include(d => d.Department).ToListAsync();
}
// GET: api/Students/5
[HttpGet("{id}")]
public async Task<ActionResult<Student>> GetStudent(int id)
{
var student = await _context.Students.Include(d => d.Department).FirstOrDefaultAsync(i => i.SId == id);
if (student == null)
{
return NotFound();
}
return student;
}
// PUT: api/Students/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task<IActionResult> PutStudent(int id, Student student)
{
if (id != student.SId)
{
return BadRequest();
}
_context.Departments.Update(student.Department);
await _context.SaveChangesAsync();
_context.Entry(student).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!StudentExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return Ok();
}
// POST: api/Students
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task<ActionResult<Student>> PostStudent(List<Student> student)
{
try
{
for (var i = 0; i < student.ToArray().Length; i++)
{
_context.Students.Add(student[i]);
}
_context.SaveChanges();
return CreatedAtAction("GetStudents", student);
}
catch
{
return BadRequest();
}
/*_context.Students.Add(student);
await _context.SaveChangesAsync();
return CreatedAtAction("GetStudent", new { id = student.SId }, student);*/
}
/*[HttpPost]
[Route("StudentList")]
public async Task<ActionResult<Student>> PostStudentList([FromBody] List<Student> student)
{
try
{
for (var i = 0; i < student.ToArray().Length; i++)
{
_context.Students.Add(student[i]);
}
_context.SaveChanges();
return CreatedAtAction("GetStudents", student);
}
catch
{
return BadRequest();
}
}*/
// DELETE: api/Students/5
[HttpDelete("{id}")]
public async Task<ActionResult<Student>> DeleteStudent(int id)
{
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return NotFound();
}
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return student;
}
private bool StudentExists(int id)
{
return _context.Students.Any(e => e.SId == id);
}
}
}
What you're looking for is probably this: https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.entityframeworkqueryableextensions.include?view=efcore-3.0 use the include method on the student context to include the department like so:
context.Students.Include(student => student.Department);
It could be that you have to update your mapping so EF understands it. In that case, in your mapping you need to include a HasOne(student => student.Department) if necessary. or in your Department mapping, use a HasMany(department => department.Students) (but your department does not have a collection of students, so the latter would not be an option unless you change the model)
Modify your Department class to include the Student navigation property
public virtual Student Student { get; set; }
then in your GetDepartments() method, include the Student property
var query = _context.Departments.Include(d => d.Student);
that returns an IQueryable, so you can add another filtering
var filteredQuery = query.Where(d => d.Student.Name.Contains("jane"));
before you return it
return query.ToList();
or
return filteredQuery.ToList();
I have tried this. But it creates circular dependency between Student and Department
As above has said that you could add Student navigation property in Department model and use Include to retrieve data.
To prevent Reference Looping (or remove circular dependency between Student and Department here),you could use below code in startup ConfigureServices:
For asp.net core 2.2:
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
For asp.net core 3.0:
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
(You need to install the package Microsoft.AspNetCore.Mvc.NewtonsoftJson firstly for 3.0)
Refer to .net core 3 not having ReferenceLoopHandling in AddJsonOptions
Related
I'm new in .NET and Web API; I create a signup user API with .NET 5.
This is my controller:
namespace SignupUsers.Controllers
{
[Route("api/signup")]
[ApiController]
public class MySignupController : ControllerBase
{
private readonly IDataRepository<UserSignup> _dataRepository;
public MySignupController(IDataRepository<UserSignup> dataRepository)
{
_dataRepository = dataRepository;
}
[HttpGet]
public IActionResult Get()
{
IEnumerable<UserSignup> userSignups = _dataRepository.GetAll();
return Ok(userSignups);
}
[HttpGet("{id}", Name = "Get")]
public IActionResult Get(int id)
{
UserSignup userSignup = _dataRepository.Get(id);
if (userSignup == null)
{
return NotFound("User Not Found!!!");
}
return Ok(userSignup);
}
[HttpPost]
public IActionResult Post([FromBody] UserSignup userSignup)
{
if (userSignup == null)
{
return BadRequest("User is null!!!");
}
_dataRepository.Add(userSignup);
return CreatedAtRoute(
"Get",
new { Id = userSignup.Id },
userSignup);
}
[HttpPut("{id}")]
public IActionResult Put(int id, [FromBody] UserSignup userSignup)
{
if (userSignup == null)
{
return BadRequest("User is null!!!");
}
UserSignup userSignupToUpdate = _dataRepository.Get(id);
if (userSignupToUpdate == null)
{
return NotFound("The User record couldn't be found");
}
_dataRepository.Update(userSignupToUpdate, userSignup);
return NoContent();
}
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
UserSignup userSignup = _dataRepository.Get(id);
if (userSignup == null)
return NotFound("The User record couldn't be found");
_dataRepository.Delete(userSignup);
return NoContent();
}
}
}
I added the DbContext to another folder:
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SignupUsers.Models;
namespace SignupUsers.Data
{
public class UserSignupDbContext : DbContext
{
public UserSignupDbContext(DbContextOptions<UserSignupDbContext> options)
: base(options)
{
}
public DbSet<Models.UserSignup> SignupUsers { get; set; }
}
}
For using code first I use EF Core.
UserSignup model class:
namespace SignupUsers.Models
{
public class UserSignup
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Password { get; set; }
}
}
For managing input and output, I use an interface and implemented that in datamanager.cs:
namespace SignupUsers.Models
{
public class DataManager : IDataRepository<UserSignup>
{
readonly UserSignupDbContext _userSignupDbContext;
public DataManager(UserSignupDbContext context)
{
_userSignupDbContext = context;
}
public void Add(UserSignup entity)
{
_userSignupDbContext.SignupUsers.Add(entity);
_userSignupDbContext.SaveChanges();
}
public void Delete(UserSignup entity)
{
_userSignupDbContext.SignupUsers.Remove(entity);
_userSignupDbContext.SaveChanges();
}
public UserSignup Get(int id)
{
return _userSignupDbContext.SignupUsers.
FirstOrDefault(s => s.Id == id);
}
public IEnumerable<UserSignup> GetAll()
{
return _userSignupDbContext.SignupUsers.ToList();
}
public void Update(UserSignup dbEntity, UserSignup entity)
{
dbEntity.Name = entity.Name;
dbEntity.Email = entity.Email;
dbEntity.Password = entity.Password;
_userSignupDbContext.SaveChanges();
}
}
}
This is my startup:
namespace SignupUsers
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<UserSignupDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("UserSignup")));
services.AddScoped<IDataRepository<UserSignup>, DataManager>();
//services.AddCors();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
When I post data to that (with Postman), I get back an error 415 (unsupported media type).
What can I do?
Thank you in advance for your help.
in postman, set the "content-type" as JSON (application/json)
I am following along with the dotnet Microsoft tutorial docs. My one disadvantage is that I'm on a Mac using VS Code. At one point in the tutorial there was a scaffolding command that did not work in VS Code. So far, everything else has worked, but I'm wondering if something is missing in my Controller and that is why I am getting this error. I copy and pasted directly from the Docs. Although, the error infers that it is a syntax issue with the try/catch statement. (which I have reviewed and everything looks in order)
Here is my Controller:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using virus_mvc.Models;
using System.Text.Encodings.Web;
using virus_mvc.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace virus_mvc.Controllers
{
public class VirusDataController : Controller
{
public async Task<IActionResult> Index()
{
return View(await _context.VirusData.ToListAsync());
}
//
// GET: /HelloWorld/Welcome/
public IActionResult Welcome(string name, int numTimes = 1)
{
ViewData["Message"] = "Hello " + name;
ViewData["NumTimes"] = numTimes;
return View();
}
// GET: VirusData/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var virus = await _context.VirusData
.FirstOrDefaultAsync(m => m.Id == id);
if (virus == null)
{
return NotFound();
}
return View(virus);
}
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var virus = await _context.VirusData.FindAsync(id);
if (virus == null)
{
return NotFound();
}
return View(virus);
}
// POST: Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price")] VirusData virus)
{
if (id != virus.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(virus);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
**if (!VirusDataExists(virus.Id))** -- this is the line throwing the error
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(virus);
}
private readonly virus_mvcContext _context;
public VirusDataController(virus_mvcContext context)
{
_context = context;
}
}
}
Here is my Error:
/Users/katie/C_sharp/virus_backend/virus_mvc/Controllers/VirusDataController.cs(89,26): error CS0103: The name 'VirusDataExists' does not exist in the current context [/Users/katie/C_sharp/virus_backend/virus_mvc/virus_mvc.csproj]
Here's my Model:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using virus_mvc.Data;
using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace virus_mvc.Models
{
public class VirusData
{
public int Id { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}
I'm learning C#.NET Core and trying to create DTO mapping without using AutoMapper as I'm working on a small project alone and want to understand fundamental before using extra packages, surpringly I could not easily find answer at stackoverflow.com or I may use wrong keyword searching.
BTW, below is my code which I successfully map to EmployeeForShortDto under GetEmployee method. Unfortunately, I don't how to map it under GetAllEmployee just because the return data is a collection, not a single record. Please advice.
EmployeeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using NetCoreWebApplication1.Dto;
using NetCoreWebApplication1.Repository;
using NetCoreWebApplication1.Other;
namespace NetCoreWebApplication1.Controller
{
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
private readonly IMasterRepository _repo;
public EmployeeController(IMasterRepository repo)
{
_repo = repo;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetEmployee(int id)
{
var data = await _repo.GetEmployee(id);
if (data == null) return NotFound();
var dataDto = new EmployeeForShortDto()
{
Id = data.Id,
EmpCode = data.EmpCode,
Fname = data.Fname,
Lname = data.Lname,
Age = NetCoreWebApplication1.Other.Extension.CalcAge(data.DateBirth)
};
return Ok(dataDto);
}
[HttpGet]
public async Task<IActionResult> GetAllEmployee()
{
var data = await _repo.GetAllEmployee();
return Ok(data);
}
}
}
MasterRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using NetCoreWebApplication1.Models;
namespace NetCoreWebApplication1.Repository
{
public class MasterRepository : IMasterRepository
{
private readonly PrDbContext _context;
public MasterRepository(PrDbContext context)
{
_context = context;
}
// Employee
public async Task<List<Employee>> GetAllEmployee()
{
var data = await _context.Employee.ToListAsync();
return data;
}
public async Task<Employee> GetEmployee(int id)
{
var data = await _context.Employee.FirstOrDefaultAsync(x => x.Id == id);
return data;
}
// Generic methods
public void Add<T>(T entity) where T : class
{
_context.Add(entity);
}
public void Delete<T>(T entity) where T : class
{
_context.Remove(entity);
}
public async Task<bool> SaveAll()
{
return await _context.SaveChangesAsync() > 0;
}
}
}
You can use an extension method to map from your entity type to your DTO type.
public static EmployeeForShortDto ToDto(this Employee employee)
{
if (employee != null)
{
return new EmployeeForShortDto
{
Id = employee.Id,
EmpCode = employee.EmpCode,
Fname = employee.Fname,
Lname = employee.Lname,
Age = NetCoreWebApplication1.Other.Extension.CalcAge(employee.DateBirth)
};
}
return null;
}
And then use where needed.
[HttpGet("{id}")]
public async Task<IActionResult> GetEmployee(int id)
{
var data = await _repo.GetEmployee(id);
if (data == null)
{
return NotFound();
}
return Ok(data.ToDto());
}
[HttpGet]
public async Task<IActionResult> GetAllEmployee()
{
var data = await _repo.GetAllEmployee();
return Ok(data.Select(x => x.ToDto()));
}
thank you for all responses, all are very useful to me. Finally, I end up with solution from #Brad. I also learned how to make a reverse mapping from DTO to a class before adding a record to the database.
I put my code below in case someone want to see. Any comments/suggestions are more than welcome. Thank you.
Extension.cs
using NetCoreWebApplication1.Dto;
using NetCoreWebApplication1.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NetCoreWebApplication1.Other
{
public static class Extension
{
public static EmployeeForShortDto MapToEmployeeForShortDto(this Employee emp)
{
if (emp != null)
{
return new EmployeeForShortDto
{
Id = emp.Id,
EmpCode = emp.EmpCode,
Fname = emp.Fname,
Lname = emp.Lname,
Age = emp.DateBirth.CalcAge()
};
}
return null;
}
public static EmployeeForListDto MapToEmployeeForListDto(this Employee emp)
{
if (emp != null)
{
return new EmployeeForListDto
{
Id = emp.Id,
EmpCode = emp.EmpCode,
Fname = emp.Fname,
Lname = emp.Lname,
Age = emp.DateBirth.CalcAge(),
EntityCode = emp.EntityCode,
IsActive = emp.IsActive
};
}
return null;
}
public static Employee MapFromEmployeeForAddDto(this EmployeeForAddDto emp)
{
if (emp != null)
{
return new Employee
{
EmpCode = emp.EmpCode,
Fname = emp.Fname,
Lname = emp.Lname,
IdCard = emp.IdCard,
IsActive = 1
};
}
return null;
}
public static int CalcAge(this DateTime? dateBirth)
{
if (dateBirth.HasValue)
{
var age = DateTime.Today.Year - dateBirth.Value.Year;
if (dateBirth.Value.AddYears(age) > DateTime.Today) age--;
return age;
}
else
{
return 0;
}
}
}
}
MasterRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using NetCoreWebApplication1.Dto;
using NetCoreWebApplication1.Models;
namespace NetCoreWebApplication1.Repository
{
public class MasterRepository : IMasterRepository
{
private readonly PrDbContext _context;
public MasterRepository(PrDbContext context)
{
_context = context;
}
// Employee
public async Task<List<Employee>> GetAllEmployee()
{
var data = await _context.Employee.ToListAsync();
return data;
}
public async Task<Employee> GetEmployee(int id)
{
var data = await _context.Employee.FirstOrDefaultAsync(x => x.Id == id);
return data;
}
public async Task<Employee> AddEmployee(Employee data)
{
await _context.Employee.AddAsync(data);
await _context.SaveChangesAsync();
return data;
}
public async Task<bool> EmployeeExists(string entityCode, string empCode)
{
if (await _context.Employee.AnyAsync(x =>
x.EntityCode == entityCode &&
x.EmpCode == empCode))
return true;
return false;
}
// Generic methods
public void Add<T>(T entity) where T : class
{
_context.Add(entity);
}
public void Delete<T>(T entity) where T : class
{
_context.Remove(entity);
}
public async Task<bool> SaveAll()
{
return await _context.SaveChangesAsync() > 0;
}
}
}
EmployeeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using NetCoreWebApplication1.Dto;
using NetCoreWebApplication1.Repository;
using NetCoreWebApplication1.Other;
using NetCoreWebApplication1.Models;
namespace NetCoreWebApplication1.Controller
{
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
private readonly IMasterRepository _repo;
public EmployeeController(IMasterRepository repo)
{
_repo = repo;
}
[HttpPost("add")]
public async Task<IActionResult> AddEmployee(EmployeeForAddDto emp)
{
if (await _repo.EmployeeExists(emp.EntityCode, emp.EmpCode))
ModelState.AddModelError("Employee", "Employee is duplicate (EntityCode + EmpCode)");
if (!ModelState.IsValid)
return BadRequest(ModelState);
Employee employeeToAdd = emp.MapFromEmployeeForAddDto();
await _repo.AddEmployee(employeeToAdd);
return StatusCode(201);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetEmployee(int id)
{
var data = await _repo.GetEmployee(id);
if (data == null) return NotFound();
return Ok(data.MapToEmployeeForShortDto());
}
[HttpGet]
public async Task<IActionResult> GetAllEmployee()
{
var data = await _repo.GetAllEmployee();
//var dataDto = data.Select(x => x.MapToEmployeeForShortDto());
var dataDto = data.Select(x => x.MapToEmployeeForListDto());
return Ok(dataDto);
}
}
}
Okay, the direct answer to your question is "do it pr returned values";
List<EmployeeForShortDto> result = new List<EmployeeForShortDto>();
foreach(Employee dbEmployee in data )
{
result.add(new EmployeeForShortDto()
{
Id = dbEmployee .Id,
EmpCode = dbEmployee .EmpCode,
Fname = dbEmployee .Fname,
Lname = dbEmployee .Lname,
Age = NetCoreWebApplication1.Other.Extension.CalcAge(dbEmployee .DateBirth)
});
}
This however, is type specific for your item. Why not make a generic method that uses reflection to map the object either by attributes attached, or by property name directly?
If you get it done, you will be able to transfer any object to a DTO, as long as you adhere to the internal rules of property names or setup up the mappings via the attributes.
For your problem, extract your implementation in a new method.
EmployeeForShortDto ConvertToDto(Employee data)
{
var dataDto = new EmployeeForShortDto()
{
Id = data.Id,
EmpCode = data.EmpCode,
Fname = data.Fname,
Lname = data.Lname,
Age = NetCoreWebApplication1.Other.Extension.CalcAge(data.DateBirth)
};
}
Then finally call it in loop,
foreach(Employee e in EmployeeList)
{
dtoList.Add(ConvertToDto(e));
}
For generic implementation, Generate a list of properties of Model and Dto via reflection. and then match their types.
class AdapterHelper<T1, T2>
{
public T1 Adapt(T2 source)
{
T1 targetItem = Activator.CreateInstance<T1>();
var props = typeof(T1).GetProperties();
var targetProps = typeof(T2).GetProperties();
foreach (var prop in props)
{
foreach (var targetProp in targetProps)
{
if (prop.Name == targetProp.Name)
{
targetProp.SetValue(targetItem, prop.GetValue(source));
//assign
}
}
}
return targetItem;
}
}
This is the link to my original answer.
I am trying to modeling a class at school, and I end up with something like this:
public class Class
{
public int ID { get; set; }
public int Grade { get; set; }
public Teacher ClassTeacher { get; set; }
}
This is the Teacher class:
public class Teacher
{
public int ID { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[DataType(DataType.Date)]
public DateTime Birthday { get; set; }
}
When I use scaffolding, migrate and update the database, this is the structure Entity Framework built for me:
dbo.Class:
ID: int
ClassTeacherID: int
Grade: int
dbo.Teacher:
ID: int
Birthday: datetime2(7)
FirstName: nvarchar(MAX)
LastName: nvarchar(MAX)
I want to display the Teacher's FirstName in Views\Classes\Details.cshtml, but the Model.ClassTeacher is null, even after I created a Teacher instance in the database and set ClassTeacherID to the newly created Teacher's ID.
Looking for your helps.
EDIT
ClassesController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace WebApplication1.Models
{
public class ClassesController : Controller
{
private readonly WebApplication1Context _context;
public ClassesController(WebApplication1Context context)
{
_context = context;
}
// GET: Classes
public async Task<IActionResult> Index()
{
return View(await _context.Class.ToListAsync());
}
// GET: Classes/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var #class = await _context.Class
.SingleOrDefaultAsync(m => m.ID == id);
if (#class == null)
{
return NotFound();
}
return View(#class);
}
// GET: Classes/Create
public IActionResult Create()
{
return View();
}
// POST: Classes/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Grade")] Class #class)
{
if (ModelState.IsValid)
{
_context.Add(#class);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(#class);
}
// GET: Classes/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var #class = await _context.Class.SingleOrDefaultAsync(m => m.ID == id);
if (#class == null)
{
return NotFound();
}
return View(#class);
}
// POST: Classes/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Grade")] Class #class)
{
if (id != #class.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(#class);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ClassExists(#class.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(#class);
}
// GET: Classes/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var #class = await _context.Class
.SingleOrDefaultAsync(m => m.ID == id);
if (#class == null)
{
return NotFound();
}
return View(#class);
}
// POST: Classes/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var #class = await _context.Class.SingleOrDefaultAsync(m => m.ID == id);
_context.Class.Remove(#class);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
private bool ClassExists(int id)
{
return _context.Class.Any(e => e.ID == id);
}
}
}
TeachersController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace WebApplication1.Models
{
public class TeachersController : Controller
{
private readonly WebApplication1Context _context;
public TeachersController(WebApplication1Context context)
{
_context = context;
}
// GET: Teachers
public async Task<IActionResult> Index()
{
return View(await _context.Teacher.ToListAsync());
}
// GET: Teachers/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var teacher = await _context.Teacher
.SingleOrDefaultAsync(m => m.ID == id);
if (teacher == null)
{
return NotFound();
}
return View(teacher);
}
// GET: Teachers/Create
public IActionResult Create()
{
return View();
}
// POST: Teachers/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,FirstName,LastName,Birthday")] Teacher teacher)
{
if (ModelState.IsValid)
{
_context.Add(teacher);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(teacher);
}
// GET: Teachers/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var teacher = await _context.Teacher.SingleOrDefaultAsync(m => m.ID == id);
if (teacher == null)
{
return NotFound();
}
return View(teacher);
}
// POST: Teachers/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,FirstName,LastName,Birthday")] Teacher teacher)
{
if (id != teacher.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(teacher);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TeacherExists(teacher.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(teacher);
}
// GET: Teachers/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var teacher = await _context.Teacher
.SingleOrDefaultAsync(m => m.ID == id);
if (teacher == null)
{
return NotFound();
}
return View(teacher);
}
// POST: Teachers/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var teacher = await _context.Teacher.SingleOrDefaultAsync(m => m.ID == id);
_context.Teacher.Remove(teacher);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
private bool TeacherExists(int id)
{
return _context.Teacher.Any(e => e.ID == id);
}
}
}
I know it's been a while since the question has been written, but you should try to Include the object reference you want.
var #class = await _context.Class.Include("Teacher")
.SingleOrDefaultAsync(m => m.ID == id);
Include will get the dependent object and put it back in your Class model.
You can chain the Include directives if you have different referenced objects.
Note : You will have to add :
using Microsoft.EntityFrameworkCore;
on top of your code to allow Include to work.
I have created a client in Visual Studio - a C# console app - to consume a Restful WebApi class. I am trying to get ALL of the records displayed besides getting a record added, updated, deleted, and display 1 record. I just need to display ALL of the records that are in the Products table.
In my Restful WebApi class, I am using migrations to seed the database. I modified the configuration.cs in my migrations folder. Added the following code in the Seed method
protected override void Seed(RestfulWebAPIExample.Models.AppDbContext context)
{
context.Products.AddOrUpdate(p => p.ID,
new Product { Name = "Milk", Price = 3.99m, Quantity = 6 },
new Product { Name = "Eggs", Price = 4.99m, Quantity = 12 },
new Product { Name = "Cheese", Price = 6.99m, Quantity = 7 },
new Product { Name = "Bacon", Price = 9.99m, Quantity = 9 }
);
}
I have the Product Model class in my client
namespace RestfulWebAPIExample
{
class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
}
Here is some of the code I have in my Program.cs in the client - to display the first record
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace WebAPIExampleClient
{
class Program
{
static void Main()
{
RunAsync().Wait();
}
static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Quantity);
}
}
}
}
}
I have been looking online for a long time and found some sites, but I could not find a real good example. Can somebody give me an example or point me in the right direction? Do I use IEnumerable somehow?
Here is my controller from the Restful WebApi class
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Description;
using RestfulWebAPIExample.Models;
namespace RestfulWebAPIExample.Controllers
{
public class ProductsController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET api/Products
public IQueryable<Product> GetProducts()
{
return db.Products;
}
// GET api/Products/5
[ResponseType(typeof(Product))]
public IHttpActionResult GetProduct(int id)
{
Product product = db.Products.Find(id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
// PUT api/Products/5
public IHttpActionResult PutProduct(int id, Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != product.ID)
{
return BadRequest();
}
db.Entry(product).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST api/Products
[ResponseType(typeof(Product))]
public IHttpActionResult PostProduct(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = product.ID }, product);
}
// DELETE api/Products/5
[ResponseType(typeof(Product))]
public IHttpActionResult DeleteProduct(int id)
{
Product product = db.Products.Find(id);
if (product == null)
{
return NotFound();
}
db.Products.Remove(product);
db.SaveChanges();
return Ok(product);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool ProductExists(int id)
{
return db.Products.Count(e => e.ID == id) > 0;
}
}
}
You are almost there, and just a few small changes and I think your problem will be solved. Change your GetProducts() method like this:
// GET api/Products
[HttpGet]
[ResponseType(IEnumerable<Product>)]
public IHttpActionResult GetProducts()
{
Ok(return db.Products);
}
Notice that I added the HttpGet attribute. You don't need to do this, but I've found that specifying it helps when you have multiple GETs in one controller. You should also look at attribute routing, as it will make things even clearer.
In your client code, you call that endpoint like this:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/products");
if (response.IsSuccessStatusCode)
{
IEnumerable<Product> products = await response.Content.ReadAsAsync<IEnumerable<Product>>();
}
}
Like I said, I think you are REALLY close.
Hope that helps.