Object null when using the same action controller

I have my GetById Action controller giving the result like this when request directly from it notice that the Breed not null (the first image)
[HttpGet("{animalId}")]
public async Task<IActionResult> GetById(int animalId, CancellationToken cancellationToken = default)
{
var animal = await _animalRepository.FindByIdAsync(animalId, cancellationToken);
return animal != null ? Ok(_mapper.Map<AnimalDTO>(animal)) : NotFound("Unable to find the requested animal");
}
[HttpGet("{animalId}")]
public async Task<IActionResult> GetById(int animalId, CancellationToken cancellationToken = default)
{
var animal = await _animalRepository.FindByIdAsync(animalId, cancellationToken);
return animal != null ? Ok(_mapper.Map<AnimalDTO>(animal)) : NotFound("Unable to find the requested animal");
}
And I have the Create Action Controller which basically using the GetById to give me the animal using the below return
return CreatedAtAction(nameof(GetById), new { Id = animal.Id }, _mapper.Map<AnimalDTO>(animal));
return CreatedAtAction(nameof(GetById), new { Id = animal.Id }, _mapper.Map<AnimalDTO>(animal));
And the second picture is the result I got which is the breed completely null
44 Replies
Angius
Angius2y ago
You need to .Include() related models Or better yet, .Select() the whole thing into a DTO So, a fast way:
var animal = await _context.Animals
.Where(a => a.Id == id)
.Include(a => a.Breed)
.FirstOrDefaultAsync();
var animal = await _context.Animals
.Where(a => a.Id == id)
.Include(a => a.Breed)
.FirstOrDefaultAsync();
and a proper way:
var animal = await _context.Animals
.Where(a => a.Id == id)
.Select(a => new AnimalDto {
Name = a.Name,
// ...
Breed = new BreedDto {
Name = a.Breed.Name
// ...
}
})
.FirstOrDefaultAsync();
var animal = await _context.Animals
.Where(a => a.Id == id)
.Select(a => new AnimalDto {
Name = a.Name,
// ...
Breed = new BreedDto {
Name = a.Breed.Name
// ...
}
})
.FirstOrDefaultAsync();
Also, reconsider using repositories altogether From what I see, your repository doesn't do anything EF wouldn't already be doing
TotechsStrypper
on the method that use return CreatedAtAction ? or the FindById ?
Angius
Angius2y ago
Where do you fetch the animal from the database? There
TotechsStrypper
findbyid let me give you the full code
Angius
Angius2y ago
My answer stays the same Wherever you fetch the animal and need its breed, you need to .Include() or .Select() it
TotechsStrypper
[HttpGet("{animalId}")]
public async Task<IActionResult> GetById(int animalId, CancellationToken cancellationToken = default)
{
var animal = await _animalRepository.FindByIdAsync(animalId, cancellationToken);
return animal != null ? Ok(_mapper.Map<AnimalDTO>(animal)) : NotFound("Unable to find the requested animal");
}
[HttpGet("{animalId}")]
public async Task<IActionResult> GetById(int animalId, CancellationToken cancellationToken = default)
{
var animal = await _animalRepository.FindByIdAsync(animalId, cancellationToken);
return animal != null ? Ok(_mapper.Map<AnimalDTO>(animal)) : NotFound("Unable to find the requested animal");
}
cs
cs
but FindByIdAsync does include the Breed
TotechsStrypper
And when call directly to it do give me the breed
Angius
Angius2y ago
Maybe your mapper doesn't map it, then
TotechsStrypper
The problem is why other method use return CreatedAtAction(nameof(GetById), new { Id = animal.Id }, _mapper.Map<AnimalDTO>(animal)); doesn't include the breed
Angius
Angius2y ago
Dunno, I loathe the repository pattern and I don't use the automapper What's the full method that returns it? Seems to me like this animal might not have the breed included
TotechsStrypper
Hope you can read it
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreatePetDTO dto, CancellationToken cancellationToken = default)
{
var listOfOwnersGuid = dto.OwnerGuids.Split(',').ToList();
var listOfOwner = new List<User>();
foreach (var ownerGuid in listOfOwnersGuid)
{
var user = await _userRepository.FindByGuidAsync(ownerGuid, cancellationToken);
if (user == null) return NotFound($"Cant find user with Guid: {ownerGuid}");
else listOfOwner.Add(user);
}
using var petaverseTransaction = await _petaverseDbContext.Database.BeginTransactionAsync();
var animal = _mapper.Map<Animal>(dto);
var breed = await _breedRepository.FindByIdIQueryable(dto.BreedId, cancellationToken).FirstOrDefaultAsync();

if(breed != null)
{
animal.SixDigitCode = await _animalRepository.Generate6DigitCodeAsync();
animal.BreedId = breed.Id;
_animalRepository.Add(animal);
await _animalRepository.SaveChangesAsync(cancellationToken);
}

if(listOfOwner.Count > 0)
{
listOfOwner.ForEach(owner => _userAnimalRepository.Add(new UserAnimal() { UserId = owner.Id, AnimalId = animal.Id}));
await _userAnimalRepository.SaveChangesAsync(cancellationToken);
}

await petaverseTransaction.CommitAsync(cancellationToken);
return CreatedAtAction(nameof(GetById), new { animalId = animal.Id }, _mapper.Map<AnimalDTO>(animal));
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreatePetDTO dto, CancellationToken cancellationToken = default)
{
var listOfOwnersGuid = dto.OwnerGuids.Split(',').ToList();
var listOfOwner = new List<User>();
foreach (var ownerGuid in listOfOwnersGuid)
{
var user = await _userRepository.FindByGuidAsync(ownerGuid, cancellationToken);
if (user == null) return NotFound($"Cant find user with Guid: {ownerGuid}");
else listOfOwner.Add(user);
}
using var petaverseTransaction = await _petaverseDbContext.Database.BeginTransactionAsync();
var animal = _mapper.Map<Animal>(dto);
var breed = await _breedRepository.FindByIdIQueryable(dto.BreedId, cancellationToken).FirstOrDefaultAsync();

if(breed != null)
{
animal.SixDigitCode = await _animalRepository.Generate6DigitCodeAsync();
animal.BreedId = breed.Id;
_animalRepository.Add(animal);
await _animalRepository.SaveChangesAsync(cancellationToken);
}

if(listOfOwner.Count > 0)
{
listOfOwner.ForEach(owner => _userAnimalRepository.Add(new UserAnimal() { UserId = owner.Id, AnimalId = animal.Id}));
await _userAnimalRepository.SaveChangesAsync(cancellationToken);
}

await petaverseTransaction.CommitAsync(cancellationToken);
return CreatedAtAction(nameof(GetById), new { animalId = animal.Id }, _mapper.Map<AnimalDTO>(animal));
}
it's ugly I know I trying to get better in writing cleaner code
Angius
Angius2y ago
You seem to be setting the BreedId of the animal not the Breed of it?
TotechsStrypper
They both return
_mapper.Map<AnimalDTO>(animal)
_mapper.Map<AnimalDTO>(animal)
Angius
Angius2y ago
animal.BreedId = breed.Id; Yeah, but you use different ways to get that animal there In the first method you use a repository, in the second you use some other demented repository-thing After the animal.BreedId = breed.Id; line add animal.Breed = breed See if that helps
TotechsStrypper
Okay I will put that after the SaveChanges()
Angius
Angius2y ago
Maybe? Idk Repositories are a mess
TotechsStrypper
because EF will create another breed if put it before the savechange
Angius
Angius2y ago
Fuck repositories All my homies hate repositories
TotechsStrypper
what's your pattern ? can you link me the blog or docs to implment your style ?
Angius
Angius2y ago
CQRS and services where needed
TotechsStrypper
services ...
Angius
Angius2y ago
Yes Instead of duplicating the functionality that DbSet already gives you for free, you create specific methods
TotechsStrypper
do you have a simple project repo ?
Angius
Angius2y ago
Afraid not
TotechsStrypper
cqrs heard that many but never understand it
Angius
Angius2y ago
But the idea is that instead of
public class ThingRepository
{
// ...
public async Task<Thing> Find(int id)
=> await _context.Things.FindAsync(id);
}
public class ThingRepository
{
// ...
public async Task<Thing> Find(int id)
=> await _context.Things.FindAsync(id);
}
which is just a useless abstraction over EF, you do
public class ThingService
{
// ...
public async Task<ThingCard> GetThingCardById(int id)
=> await _context.Things
.Where(t => t.Id == id)
.Where( ... )
.Select(t => new ThingCard { ... })
.FirstOrDefaultAsync();
}
public class ThingService
{
// ...
public async Task<ThingCard> GetThingCardById(int id)
=> await _context.Things
.Where(t => t.Id == id)
.Where( ... )
.Select(t => new ThingCard { ... })
.FirstOrDefaultAsync();
}
etc The idea behind services
TotechsStrypper
is Unit Of Work also a service ?
TotechsStrypper
woah you totally right
Angius
Angius2y ago
EF already implements UoW
TotechsStrypper
aww man CreateAtAction suck I thought it just route the action and all the behavior stay the same
Angius
Angius2y ago
Nope, it doesn't call the actual action Just constructs the URL and returns some data
TotechsStrypper
but aint it suppose to do the same ? just pass the parameter call the right route and define what it need to to return ? If have some additional logic inside GetById. It will not work ?
Angius
Angius2y ago
¯\_(ツ)_/¯ Here's a CQRS sample from one of my projects: https://github.com/Atulin/CQRS-Sample Press the . key when you open the repo in the browser so you can browse the code more easily
TotechsStrypper
did you just create a freaking repo for like 5 minutes ?
Angius
Angius2y ago
Yeah, why? git init && git add . && git commit -am "init" already gets you halfway there lol
TotechsStrypper
you re freaking beast
Angius
Angius2y ago
Ah, the BaseHandler might be a bit confusing, but all it is is just provides some shims to handle returns types easier. It's just a bunch of
public OkResult Ok() => new();
public OkObjectResult Ok(object o) => new(o);
public OkResult Ok() => new();
public OkObjectResult Ok(object o) => new(o);
methods
TotechsStrypper
you mean the return CreatedAtAction(nameof(GetById), new { Id = animal.Id }, _mapper.Map<AnimalDTO>(animal)); ?
Angius
Angius2y ago
Nope, in the example code I sent you All the Handler classes inherit from BaseHandler that isn't a standard MediatR class, thought I'll clarify
TotechsStrypper
okay let me see Mediator is that cqrs thingy ?
Angius
Angius2y ago
It's a library that makes CQRS easier Very commonly used. In fact, I don't know anybody who wouldn't use it for CQRS lol
TotechsStrypper
ohhhhhhhhhhhhhhh I see now say thanks for helping me all these time can I have friend request ?
Angius
Angius2y ago
You can ping me if you need help