Getting navigation properties of your entity when you return a CreatedAtRouteResult - asp.net-core

Request:
namespace mediere_API.Requests
{
public class LocalitateRequest
{
public string Nume { get; set; }
public int JudetId { get; set; }
}
}
DTO
namespace mediere_API.Dtos
{
public class LocalitateDTO
{
public int Id { get; set; }
public string Nume { get; set; }
public JudetDTO Judet { get; set; }
}
}
Entity
using mediere_API.Dtos;
using System.ComponentModel.DataAnnotations;
namespace mediere_API.DataLayer.Entities
{
public class Localitate : BaseEntity
{
[Required]
public string Nume { get; set; }
[Required]
public int JudetId { get; set; }
public virtual Judet judet { get; set; }
public Localitate() { }
}
}
Processor method
async Task<ActionResult> ILocalitatiProcessor.AddLocalitate(LocalitateRequest localitateRequest)
{
var record = _mapper.Map<Localitate>(localitateRequest);
_unitOfWork.Localitati.Insert(record);
if (await _unitOfWork.SaveChangesAsync() == false)
{
return new BadRequestResult();
}
return new CreatedAtRouteResult("GetByIdLocalitate", new {Id = record.Id}, _mapper.Map<LocalitateDTO>(record));
}
So, I have these pieces of code.
The way I'm using my front-end, I need to have the navigation properties filled in when I get the response on the POST request.
Right now I get:
{
"id": 12777,
"nume": "test",
"judet": null
}
On the get requests it works properly, but with CreatedAtRouteResult it doesn't, and I know why, but I don't know how should I fix it.
Record doesn't have the navigation properties filled in because it is a mapping of localitateRequest (which doesn't have the navigation properties) to Localitate.
So, how should I approach this problem?
Thanks.

Related

How to Map DTO class to "Model" class In generic Repository

I use DTO class in API layer and I struggle to map DTO class to "model" class in generic Repository.cs in core layer.
Repository.cs :
namespace DTOMap.Core.Repository.Generic
{
public class Repository<T> : IRepository<T> where T : class
{
private DTOMapContext _context;
private DbSet<T> _table;
private IMapper _mapper;
public Repository(DTOMapContext context)
{
_context = context;
_table = _context.Set<T>();
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MyMapper>();
});
_mapper = config.CreateMapper();
}
public T Add(T obj)
{
// Here how to use My Mapper to save a book or an author generically
// Sth like :
// temp = _table.Add(_mapper.Map<T>(obj)); Here I want to map Dto to model to save in the db
// return = (_mapper.Map<T>(temp)); Here I want to map Model to DTO to collect it in API
// but I can't have a reference to TDTO
throw new NotImplementedException();
}
}
}
I show you the other classes that I find useful (I only implement Add function for this example and I am a beginner in .Net) :
Author.cs
namespace DTOMap.Core.Models
{
[Table("Author")]
internal class Author
{
[Key]
public int id { get; set; }
[Required, MaxLength(255)]
public string firstName { get; set; }
[Required,MaxLength(255)]
public string lastName { get; set; }
}
}
Book.cs
namespace DTOMap.Core.Models
{
[Table("Book")]
internal class Book
{
[Key]
public int id { get; set; }
[Required,MaxLength(255)]
public string name { get; set; }
[Required]
public int authorId { get; set; }
[Required]
public Author author { get; set; }
}
}
AuthorDTO.cs
namespace DTOMap.Domain.DTO
{
public class AuthorDTO
{
public int id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
}
BookDTO.cs
namespace DTOMap.Domain.DTO
{
public class BookDTO
{
public int id { get; set; }
public string name { get; set; }
public int authorId { get; set; }
public AuthorDTO author { get; set; }
}
}
IRepository.cs
namespace DTOMap.Domain.Interface
{
public interface IRepository<T>
{
T Add(T obj);
}
}
MyMapper.cs
namespace DTOMap.Core
{
public class MyMapper : Profile
{
public MyMapper()
{
CreateMap<Book, BookDTO>();
CreateMap<BookDTO, Book>();
CreateMap<Author, AuthorDTO>();
CreateMap<AuthorDTO, Author>();
}
}
}
program.cs
... Some Fcts
builder.Services.AddTransient<IRepository<BookDTO>, BookRepository>();
builder.Services.AddTransient<IRepository<AuthorDTO>, AuthorRepository>();
... Some Fcts
If you need any other information, please ask me.

How to update an existing entity that has a nested list of entities?

I'm trying to update an entity using entity framework but, everytime I try to do it, it raises an error saying that a nested entity the main class contains cannot be tracked.
These are my classes:
public abstract class BaseEntity
{
public int Id { get; set; }
}
public class Dashboard : BaseEntity
{
public int Order { get; set; }
public string Title { get; set; }
public bool Enabled { get; set; }
public virtual ICollection<Submenu> Submenu { get; set; }
}
public class Submenu : BaseEntity
{
public int Order { get; set; }
public bool Enabled { get; set; }
public string Title { get; set; }
public string Image { get; set; }
public string Descriptions { get; set; }
public virtual ICollection<Action> Actions { get; set; }
public int DashboardId { get; set; }
public virtual Dashboard Dashboard { get; set; }
}
public class Action : BaseEntity
{
public string Type { get; set; }
public string Label { get; set; }
public string Url { get; set; }
public string Extension { get; set; }
public virtual Submenu Submenu { get; set; }
public int SubmenuId { get; set; }
}
The one I am using to update is Dashboard, which contains the rest of the classes.
I'm trying to do it using a generic service layer and a generic repository that are defined this way:
public class GenericService<T> : IGenericService<T> where T : BaseEntity
{
private readonly IBaseRepository<T> baseRepository;
public GenericService(IBaseRepository<T> baseRepository)
{
this.baseRepository = baseRepository;
}
public async Task Update(T entity, T attachedEntity)
{
await baseRepository.Update(entity, attachedEntity);
}
}
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly PortalContext dataContext;
private DbSet<T> DbSet { get; set; }
public BaseRepository(PortalContext context)
{
dataContext = context;
DbSet = dataContext.Set<T>();
}
public async Task Update(T entity, T attachedEntity)
{
dataContext.Entry(attachedEntity).State = EntityState.Detached;
DbSet.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
await dataContext.SaveChangesAsync();
}
}
And, at last but no least, this is the way I am configuring everything at Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PortalContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("PortalContext"))
);
services.AddTransient(typeof(IGenericService<>), typeof(GenericService<>));
services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddTransient<Func<string, ClaimsPrincipal, IRoleCheck>>((serviceProvider) =>
{
return (controllerName, claimsPrincipal) =>
new RoleCheck(serviceProvider.GetRequiredService<IGenericService<Dossier>>(),
serviceProvider.GetRequiredService<IGenericService<DossierTemplate>>(),
serviceProvider.GetRequiredService<IGenericService<Dashboard>>(),
controllerName, claimsPrincipal);
});
}
What the application first does is calling the RoleCheck class to retrieve and filter the required entities and, after that, the user can update them.
When I call the update function at the controller
public async Task<ActionResult<Dashboard>> Put(int id, [FromBody] Dashboard dashboard)
{
var currentDashboard = await service.Get(id);
if (currentDashboard == null)
{
return NotFound();
}
await service.Update(dashboard, currentDashboard);
return Ok();
}
I always receive the next error at the repository:
error
Is there something I am doing wrong? I have been stuck with this for a week now...
Thanks in advance and sorry for the long text, but I wanted it to be clear.
I could finally solve it by adding .AsNoTracking() at the Get() method of my repository:
public async Task<T> Get(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> includes)
{
IQueryable <T> query = DbSet.AsNoTracking();
if (includes != null)
{
query = includes(query);
}
return await query.FirstOrDefaultAsync(m => m.Id == id);
}

In my Web Api how to write code for if MobileItems(Parent Class) in which a single mobile item has AccessoryItems(Child)?

public class MobileItems
{
public int MobileItemsId { get; set; }
public string MobileName { get; set; }
public int MobilePrice { get; set; }
public string isAccessory { get; set; }
public List<AccessoryItems> AccessoryItems { get; set; }
}
}
This is my Parent Model MobileItems in which i have to create a method which checks for every mobile that it has belonging accessory or not if it has then print yes or else no
This looks like the item class and not the class that collects all of the mobiles.
public class MobileItems
{
public int MobileItemsId { get; set; }
public string MobileName { get; set; }
public int MobilePrice { get; set; }
public bool hasAccessory
{
get
{
return AccessoryItems.Any();
}
}
public List<AccessoryItems> AccessoryItems { get; set; }
}
Notice that I changed isAccessory to hasAccessory, because (if I understand things correctly) the mobile is not an accessory, it has an accessory.
hasAccessory will return true if there is any object in the AccessoryItems list. Be careful with null though. You probably wanna initialize AccessoryItems in the constructor to prevent future exceptions.
You can use this in some other class or method in order to select what to print.
public void PrintMobileHasAccessory(List<MobileItems> mobiles)
{
foreach (MobileItems mobile in mobiles)
{
if (mobile.hasAccessory)
Console.WriteLine("yes");
else
Console.WriteLine("no");
}
}

NullObjectReference when performing a PUT in Web Api

I am trying to update a variable inside a class "Conversation" put the parameter objects are not binding for some reason. I don't know why not. As a result I am getting this error:
"Object reference not set to an instance of an object.",
Controller:
[HttpPut]
public async Task<IHttpActionResult> AddMember(string key, User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Conversation conver = await db.Conversations.FindAsync(key); //THIS IS NULL
if (conver == null)
{
return NotFound(); //METHOD IS ENDING HERE
}
conver.Members.Add(user); //THIS IS NULL
db.Entry(conver).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
return NotFound();
}
return StatusCode(HttpStatusCode.NoContent);
}
Model class User:
namespace AcademicAssistant.Models
{
[DataContract]
public class User
{
[Key]
[DataMember]
public string Email { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public bool Admin { get; set; }
}
}
Model Class Conversation:
[DataContract]
public class Conversation
{
[Key]
[DataMember]
public string Key { get; set; }
[DataMember]
public string ConversationName { get; set; }
[DataMember]
public string Administrator { get; set; }
[DataMember]
public virtual ICollection<User> Members { get; set; }
[DataMember]
public virtual ICollection<Message> Messages { get; set; }
}
I am testing this in Postman like this:
ROUTE = api/Conversations/AddMember/?key="1LM4"
JSON = {"user": { "Email": "56#56.com", "Password" : "Passw-1", "Admin" : true } }
SOLUTION:
The Route should have been
api/Conversations/AddMember/?key=1LM4
and the JSON formatted like this:
{
"Email": "56#56.com",
"Password" : "Passw-1",
"Admin" : true
}

MVC4 - Controller Scaffolding, Custom Data context class issue: Unable to retrieve metadata

When I try to create a controller by scaffolding, I get the following error:
Unable to retrieve metadata for 'TurkUp.Models.Admin.CreateCourseViewModel'. Schema specified is not valid. Errors:
The mapping of CLR type EDM is ambiguous because multiple CLR types match the EDM type 'Coursework'.
Here is the code for the model:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace TorkUp.Models.Admin
{
public class CreateCourseViewModel
{
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[Required]
public string Title { get; set; }
}
}
The custom data context class:
using System.Data.Entity;
using System.Linq;
using TorkUp.ClassLibrary;
using TorkUp.ClassLibrary.Admin;
using TorkUp.ClassLibrary.User;
namespace TorkUp.Infrastructure
{
public class UniversityDb : DbContext, IUniversityDataSource
{
public UniversityDb() : base("DefaultConnection") { }
// Admin data
public DbSet<Course> Courses { get; set; }
public DbSet<Class> Classes { get; set; }
public DbSet<Coursework> Courseworks { get; set; }
public DbSet<Student> Students { get; set; }
// User data
public DbSet<Assignment> Assignments { get; set; }
public DbSet<Task> Tasks { get; set; }
// Admin data
IQueryable<Course> IUniversityDataSource.Courses { get { return Courses; } }
IQueryable<Class> IUniversityDataSource.Classes { get { return Classes; } }
IQueryable<Coursework> IUniversityDataSource.Courseworks { get { return Courseworks; } }
IQueryable<Student> IUniversityDataSource.Students { get { return Students; } }
// User data
IQueryable<Assignment> IUniversityDataSource.Assignments { get { return Assignments; } }
IQueryable<Task> IUniversityDataSource.Tasks { get { return Tasks; } }
// Save to database
void IUniversityDataSource.Save() { SaveChanges(); }
}
}
And the class for the course:
using System.Collections.Generic;
namespace TorkUp.ClassLibrary.Admin
{
public class Course
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<Class> Classes { get; set; }
public ICollection<Coursework> Courseworks { get; set; }
}
}
If you have this error, it is because in you DbContext:
// Admin data
public DbSet<Course> Courses { get; set; }
public DbSet<Class> Classes { get; set; }
public DbSet<Coursework> Courseworks { get; set; } //you use here Coursework class to create a DbSet. But when you scaffold you use CreateCourseViewModel.
public DbSet<Student> Students { get; set; }
If you want to use CreateCourseViewModel, then you have to change this line above. But I think as you put ViewModel suffix the purpose of CreatecourseViewModel is to wrap another classes for displaying in the view.