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.
Related
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 have a model called notes that I am feeding into a kendo grid via calls to an interface / repository class. Everything works but it is running synchronously and I want to run it asynchronously.
I'm using .NET core 3.1 so IAsyncEnumerable etc should all be available if I can work out how to do it. I've tried a lot of variations but always get errors. Any help much appreciated.
This is the interface
namespace FliveRetry.Models.PTs
{
public interface IPtNoteRepository
{
IEnumerable<Note> GetAllNotes();
Note GetNoteById(int NoteId);
}
}
This is the repository
namespace FliveRetry.Models.PTs
{
public class PtNoteRepository : IPtNoteRepository
{
private readonly FliveRetryContext context;
public PtNoteRepository(FliveRetryContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
public IEnumerable<Note> GetAllNotes()
{
return context.Note;
}
public Note GetNoteById(int itemId)
{
var note = context.Note.SingleOrDefault(i => i.ID == itemId);
return note;
}
}
}
and this is the index model where I'm calling it and feeding it to the grid via OnPostRead
namespace FliveRetry.Pages.Notes
{
public class IndexModel : NoteSelectPageModel
{
private const int CURRENT_USER_ID = 21; //Fake user id for demo
private readonly IPtNoteRepository rpsNotesRepo;
public static IList<Note> Notes { get; set; }
[BindProperty(SupportsGet = true)]
public NoteScreenEnum? PresetScreen { get; set; }
public IndexModel(IPtNoteRepository rpsNotesData)
{
rpsNotesRepo = rpsNotesData;
}
public void OnGet()
{
IEnumerable<Note> notes;
switch (PresetScreen)
{
case NoteScreenEnum.GeneralNotes:
notes = rpsNotesRepo.GetAllNotes();
break;
case NoteScreenEnum.ThisNote:
notes = rpsNotesRepo.GetNoteByID(CURRENT_USER_ID);
break;
default:
notes = rpsNotesRepo.GetAllNotes();
break;
}
Notes = notes.ToList();
}
public JsonResult OnPostRead([DataSourceRequest] DataSourceRequest request)
{
return new JsonResult(Notes.ToDataSourceResult(request));
}
}
}
In other pages like create or edit.cshtml.cs for example I am successfully using async to edit and create, e.g:
namespace FliveRetry.Pages.Notes
{
public class EditModel : NoteSelectPageModel
{
private readonly FliveRetry.Data.FliveRetryContext _context;
public EditModel(FliveRetry.Data.FliveRetryContext context)
{
_context = context;
}
[BindProperty]
public Note Note { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Note = await _context.Note
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Note == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(IFormCollection form, int? id, string[] selectedOrgs, string[] selectedClients, string[] selectedStaffs, string[] selectedNoteTypes)
{
if (!ModelState.IsValid)
{
return Page();
}
var noteToUpdate = await _context.Note
.FirstOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<Note>(noteToUpdate, "note", // Prefix for form value.
c => c.Title, c => c.NoteText, c => c.NoteDate, c => c.Amount, c => c.ImageURL, c => c.FileURL, c => c.Archived, c => c.DateSaved, c => c.UserID, c => c.StartTime, c => c.FinishTime))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
}
}
Try to use below code to convert synchronous action to asynchronous action:
IPtNoteRepository:
public interface IPtNoteRepository
{
Task<IEnumerable<Note>> GetAllNotesAsync();
Task<Note> GetNoteByIdAsync(int NoteId);
}
Repository:
public class PtNoteRepository : IPtNoteRepository
{
private readonly FliveRetryContext context;
public PtNoteRepository(FliveRetryContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
public async Task<IEnumerable<Note>> GetAllNotesAsync()
{
return await context.Note.ToListAsync();
}
public async Task<Note> GetNoteByIdAsync(int itemId)
{
var note = await context.Note.SingleOrDefaultAsync(i => i.ID == itemId);
return note;
}
}
IndexModel:
public async Task OnGetAsync()
{
IEnumerable<Note> notes;
switch (PresetScreen)
{
case NoteScreenEnum.GeneralNotes:
notes = await rpsNotesRepo.GetAllNotesAsync();
break;
case NoteScreenEnum.ThisNote:
notes = await rpsNotesRepo.GetNoteByIdAsync(CURRENT_USER_ID);
break;
default:
notes = await rpsNotesRepo.GetAllNotesAsync();
break;
}
Notes = notes.ToList();
}
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
I am learning about Unit of work, repository and service. Now I don't know why the values entered seems to be null while login in screen
After values are entered when i give submit it shows only null in values screen
while breakpoints in controller i found that there is null.
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Model;
using ClassLibrary1;
namespace Registeration.Controllers
{
public class DbController : Controller
{
// GET: Db
public ActionResult Index()
{
Service obj = new Service();
var lis = obj.GetAllLogins();
return View(lis);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Mdl Obj)
{
Service obj1 = new Service();
var details = obj1.CreateEmp(Obj);
return RedirectToAction("Index");
}
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(string userName, string dpt)
{
Service obj = new Service();
var res = obj.Login(userName, dpt); //values shows null
if (res != null)
{
return RedirectToAction("Index");
}
return RedirectToAction("Login");
}
}
}
Service Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using DataAccess;
using DataAccess.UoW;
using Model;
namespace ClassLibrary1
{
public class Service
{
private readonly Unit _uow;
public Service()
{
_uow = new Unit();
}
public IEnumerable<Mdl> GetAllLogins()
{
var logins = _uow.Register.GetAll().ToList();
if (logins.Any())
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Table_1, Mdl>();
});
IMapper mapper = config.CreateMapper();
var dest = mapper.Map<List<Table_1>, List<Mdl>>(logins);
return dest;
}
return null;
}
public Mdl Login(string userName,string dpt)
{
var logins = _uow.Register.Get(x=>x.Name==userName && x.Deprmt==dpt);
if (logins != null)
{
var obj1 = new Mdl();
if (logins.Id > 0)
return obj1;
}
return null;
}
}
}
Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
public class Mdl
{
public int Id { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
public string Deprmt { get; set; }
public string State { get; set; }
}
}
You have not shown you view code here, If it on login[POST] method then, it may be possible you are not using name attribute in your html control.
Controls should have the same name as your params(userName, dpt) so that ModelBinder can bind them.
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.