E.F Core and Automapper not getting all data when getting by id

729 views Asked by At

I'm using EF Core and Auto Mapper in an ASP.NET CORE api and I am having a little bit of trouble understanding why I am not getting all my data when I get it by ID.

When I debug and look at the data it is all there in the variable but when it hits the automapper part it is like some data is ignored.

How can I get all the data with the corresponding Id.

this is my method for the GetAsset by Id.

[HttpGet("{key:int}", Name = "getAsset")]
        [EnableQuery]
        public async Task<ActionResult<AssetDto>> GetAsset(int key)
        {
            var asset = await _context.Assets.Include(c => c.Category)
                .Include(m => m.Manufacturer)
                .Include(c => c.Condition)
                .Include(l => l.Location)
                .Include(f => f.AssetFiles)
                .FirstOrDefaultAsync(i => i.Id == key);
            
            if (asset == null) return NotFound();

            var assetDto = _mapper.Map<AssetDto>(asset);
            return assetDto;
        }

Here is my mapping with automapper

CreateMap<Asset, AssetDto>().ReverseMap();

here is my AssetDTO


public class AssetDto
    {   //  Get and sets the Id property
        public int Id { get; set; }
        // Gets and sets the Name Property and sets it to required
        [Required]
        public string Name { get; set; }
        // Gets and sets the CategoryId property
        public int CategoryId { get; set; }
        // Gets and sets the CategoryName property set it to NotMapped
        [NotMapped]
        public string CategoryName { get; set; }
        // Gets and sets the ManufacturerId property
        public int ManufacturerId { get; set; }
        // Gets and sets the ManufacturerName property set it to NotMapped
        [NotMapped]
        public string ManufacturerName { get; set; }
        //Gets and sets the Model property
        public string Model { get; set; }
        //Gets and sets the SerialNumber property
        public string SerialNumber { get; set; }
        // Gets and sets the PurchasePlace property
        public string PurchasePlace { get; set; }
        // Gets and sets the Quantity property
        public int Quantity { get; set; }
        // Gets and sets the AcquiredDate property
        public DateTime AcquiredDate { get; set; }
        // Gets and sets the PurchasePrice property/
        public float PurchasePrice { get; set; }
        // Gets and sets the CurrentValue property
        public float CurrentValue { get; set; }
        //Gets and sets the ConditionId property
        public int ConditionId { get; set; }
        // Gets and sets the ConditionName property set it to NotMapped/
        [NotMapped]
        public string ConditionName { get; set; }
        // Gets and sets the LocationId property
        public int LocationId { get; set; }
        // Gets and sets the LocationName property set it to NotMapped
        [NotMapped]
        public string LocationName { get; set; }
        // Gets and sets the RetiredDate property
        public DateTime RetiredDate { get; set; }
        // Gets and sets the Description property
        public string Description { get; set; }
        // Gets and sets the Files property set it to NotMapped
        [NotMapped]
        public ICollection<AssetFileDto> Files { get; set; } <-- updated
    }

and my Asset Model

public class Asset
    {   // Int property that gets/sets id
        public int Id { get; set; }
        // String property that gets/sets Name
        [Required]
        public string Name { get; set; }
        //Foreign Key
        //Int property that gets/sets category id
        public int? CategoryId { get; set; }
        // Foreign Key
        // Int property that gets/sets Manufacturer id
        public int? ManufacturerId { get; set; }
        // String property that gets/sets model
        public string Model { get; set; }
        // String property that gets/sets serial number
        public string SerialNumber { get; set; }
        // String property that gets/sets purchase place
        public string PurchasePlace { get; set; }
        //Int property that gets/sets quantity
        public int? Quantity { get; set; }
        // Datetime property that gets/sets acquired date
        public DateTime AcquiredDate { get; set; }
        // Float property that gets/sets purchase price
        public float? PurchasePrice { get; set; } 
        //Float property that gets/sets current value
        public float? CurrentValue { get; set; }
        // Foreign Key
        // Int property that gets/sets condition id
        public int? ConditionId { get; set; }
        // Foreign Key
        // Int property that gets/sets location id
        public int? LocationId { get; set; }
        // Datetime property that gets/sets retired date
        public DateTime RetiredDate { get; set; }
        //String property that gets/sets description
        public string Description { get; set; }
        // Foreign Key
        // Collection of asset files
        public ICollection<AssetFile> AssetFiles { get; set; }
        // Navigation Property
        public Category Category { get; set; }
        // Navigation Property
        public Manufacturer Manufacturer { get; set; }
        // Navigation Property
        public Condition Condition { get; set; }
        //Navigation Property
        public Location Location { get; set; }
    } 

The data that I am missing is the CategoryName, ManufacturerName, ConditionName LocationName, And All the asset files that are connected to the asset

When I do a normal get request in postman I get all the data no problem. But when getting it by ID the above fields are missing and I don't know why

any guidance would be grateful

Update:

assetFile DTO

public class AssetFileDto
    {
        // Int property that gets/sets the id
        public int Id { get; set; }
        // Byte property that gets/sets the files
        public byte[] Files { get; set; }
        // String property that gets/sets the name
        public string Name { get; set; }
        // String property that gets/sets the mime type
        public string MimeType { get; set; }
    }

assetFile.cs

public class AssetFile
    {
        // Int property that gets/sets the id
        public int? Id { get; set; }
        // Byte property that gets/sets the File
        public byte[] File { get; set; }
        // String property that gets/sets Name
        public string Name { get; set; }
        // String property that gets/sets the mime type
        public string MimeType { get; set; }
        //Int property that gets/sets the asset id 
        public int AssetId { get; set; }
        // Asset navigation property
        public Asset Asset { get; set; }
    }
1

There are 1 answers

9
Shoejep On BEST ANSWER

You need to expand on your mapping profile. AutoMapper can't work everything out for you. It only maps the properties with the same names automatically.

For example, if you want CategoryName

CreateMap<Asset, AssetDto>()
  .ForMember(x => x.CategoryName, x => x.MapFrom(y => y.Category.Name))
  .ReverseMap();

It's not directly related to this question, but I would recommend that you use ProjectTo so you only select the fields you need, instead of loading them into memory and then filtering.


I checked to see if it worked by added your entities to a local project. Some of the stuff I changed was for part of the demo, so you can just notice the changes and then apply the relevant ones to your code.

Controller / Service

Inject IMapper and use ProjectTo

(you'll need AutoMapper.Extensions.Microsoft.DependencyInjection, if you don't have it already).

private readonly IMapper Mapper;

/* Inject into controller constructor */

public async Task<IActionResult> GetAsset(int key)
{
    var asset = await Db.Assets
            .Where(i => i.Id == key)
            .ProjectTo<AssetDto>(Mapper.ConfigurationProvider)
            .FirstOrDefaultAsync();

    if (asset == null) return NotFound();

    return Json(asset);
}

AssetFileDto

If you want it to match with your AssetFile, Files should be called File.

 public byte[] File { get; set; }

Mapping Profile

You should then have a mapping profile that looks something like this

 CreateMap<Asset, AssetDto>()
     .ForMember(x => x.Files, x => x.MapFrom(y => y.AssetFiles));

 CreateMap<AssetFile, AssetFileDto>();