Christopher Drzal Christopher Drzal - 21 days ago 5
C# Question

AutoMapper not mapping object within object

AutoMapper is currently not pulling down the

IpAddress
object within my
Server
object. The end goal is go have my input already populated with the current IP Address in my edit view. However, when I place a break point in my Controller, the
IpAddress
property is null so I can't get the actual address out of it. I know I am doing something incorrectly with AutoMapper, but haven't been able to find a specific tutorial yet. Thanks in advance!

Break point screenie

ServersController.cs

public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}

var server = Mapper.Map<ServerViewModel>(await _context.Servers.SingleOrDefaultAsync(m => m.Id == id));
if (server == null)
{
return NotFound();
}
return View(server);
}


ServerViewModel.cs

public class ServerViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public OperatingSystem OS { get; set; }
public MachineType MachineType { get; set; }
public string AdminUserName { get; set; }
public string AdminPassword { get; set; }
public string EsxHost { get; set; }
public int IpAddressId { get; set; }
public IpAddress IpAddress { get; set; }

public List<SelectListItem> GetOperatingSystems()
{
List<SelectListItem> operatingSystems = new List<SelectListItem>();
Array values = Enum.GetValues(typeof(OperatingSystem));
operatingSystems.Add(new SelectListItem
{
Text = "Select",
Value = ""
});

foreach(OperatingSystem val in values)
{
operatingSystems.Add(new SelectListItem
{
Text = val.ToString(),
Value = val.ToString()
});
}
return operatingSystems;
}

public List<SelectListItem> GetMachineTypes()
{
List<SelectListItem> machineTypes = new List<SelectListItem>();
Array values = Enum.GetValues(typeof(MachineType));
machineTypes.Add(new SelectListItem
{
Text = "Select",
Value = ""
});

foreach (MachineType val in values)
{
machineTypes.Add(new SelectListItem
{
Text = val.ToString(),
Value = val.ToString()
});
}
return machineTypes;
}
}


Server.cs

public class Server
{
[Key]
[Required]
public int Id { get; set; }
public string Name { get; set; }
public OperatingSystem OS { get; set; }
[Display(Name = "Machine Type")]
public MachineType MachineType { get; set; }
[Display(Name = "Admin User Name")]
public string AdminUserName { get; set; }
[Display(Name = "Admin Password")]
public string AdminPassword { get; set; }
[Display(Name = "ESX Host")]
public string EsxHost { get; set; }
public int IpAddressId { get; set; }
[ForeignKey("IpAddressId")]
public IpAddress IpAddress { get; set; }
}


Startup.cs

Mapper.Initialize(config =>
{
config.CreateMap<IpAddressViewModel, IpAddress>().ReverseMap();
config.CreateMap<ServerViewModel, Server>().ReverseMap();
});


EDIT: After some reading, I have discovered that I can add a
.Include()
clause to my query to bring in the
IpAddress
object. My new
ServersController.cs
code is below. However, I am still a little confused. Isn't this the type of thing that Automapper was meant to take care of automatically? Am I missing the entire point of Automapper?

NEW ServersController.cs

public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}

var server = Mapper.Map<ServerViewModel>(await _context.Servers.Include(s => s.IpAddress).SingleOrDefaultAsync(m => m.Id == id));

if (server == null)
{
return NotFound();
}
return View(server);
}

Answer

I am going to go ahead and answer this to put some closure on it.

The key was adding a .Include() clause to my LINQ query to pull in objects attached to the parent object.

The correct way to do this is in my EDIT section above. Further examples can be found here.