Rookian
Rookian

Reputation: 20559

AutoMapper : Map a many-to-many association into a DTO (Is flattening of collection items supported?)

I have a many to many association between a Team and an Employee.

public class Employee : Entity
{
    public virtual string LastName { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string EMail { get; set; }
    public virtual IList<LoanedItem> LoanedItems { get; private set; }
    public virtual ISet<Team> Teams { get; private set; }

    public Employee()
    {
        if (LoanedItems == null)
        {
            LoanedItems = new List<LoanedItem>();
        }

        if (Teams == null)
        {
            Teams = new HashedSet<Team>();
        }
    }

    public virtual Employee AddTeam(Team team)
    {
        Teams.Add(team);
        team.Employees.Add(this);

        return this;
    }

    public virtual Employee RemoveTeamFromEmployee(Team team)
    {
        Teams.Remove(team);
        team.Employees.Remove(this);

        return this;
    }
}

public class Team : Entity
{
    public virtual string Name { get; set; }
    public virtual ISet<Employee> Employees { get; private set; }

    public Team()
    {
        if (Employees == null)
        {
            Employees = new HashedSet<Employee>();
        }
    }

    public virtual Team RemoveEmployeeFromTeam (Employee employee)
    {
        Employees.Remove(employee);
        employee.Teams.Remove(this);

        return this;
    }

    public virtual Team AddEmployee(Employee employee)
    {
        Employees.Add(employee);
        employee.Teams.Add(this);

        return this;
    }
}

I want to map this into the following DTO.

public class EmployeeForm
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string EMail { get; set; }
    public string TeamName { get; set; }
    public int Id { get; set; }
}

So the TeamName is the one who has to be mapped.

I use the following mapping code:

Mapper.CreateMap<Employee, EmployeeForm>()
    .ForMember(dest=>dest.TeamName, opt => opt.MapFrom(s=>s.Teams.FirstOrDefault().Name));
var mappedresult = Mapper.Map<List<Employee>, List<EmployeeForm>>(result);

The problem is the Employee.Teams member. The Teams collection is eager loaded by the way. So it is there.

When Teams is Null I get a NullReference Exception.

How can I map the name of a team into the DTO?

EDIT: For now I left Automapper and use this cumbersome code instead:

var result = _repository.GetAllView();

var employeeForms = new List<EmployeeForm>();
foreach (Employee employee in result)
{
    var employeeAdded = false;
    foreach (var team in employee.Teams)
    {
        employeeForms.Add(new EmployeeForm
          {
              EMail = employee.EMail,
              FirstName = employee.FirstName,
              LastName = employee.LastName,
              TeamName = team.Name
          });
        employeeAdded = true;
    }

    if (!employeeAdded)
    {
        employeeForms.Add(new EmployeeForm
        {
            EMail = employee.EMail,
            FirstName = employee.FirstName,
            LastName = employee.LastName,
            TeamName = string.Empty
        });
    }
}
return View(employeeForms);

For me this smells pentrating ...

Upvotes: 0

Views: 2179

Answers (1)

Paco
Paco

Reputation: 8381

You get the nullreferenceexception because of FirstOrDefault().Name, so you have to do a null check after firstordefault and tell automapper what you want to return when null. The other parts should workf fine.

Upvotes: 2

Related Questions