Reputation: 95
I've been trying to get this working for too long. I've looked at every automapper question I could find, but still no luck.
How do I configure Automapper so that it will properly map my many to many properties between my business entity and my database model?
I'm using a repository pattern using a DB first data model.
I have these entity objects:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { } }
public List<Department> Departments { get; set; }
public List<Company> Companies { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { } }
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { } }
}
I need to be able to update these. I need to map them to the db object so I can update the user.
public partial class User{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public virtual ICollection<UsersCompany> UsersCompanies { get; set; }
public virtual ICollection<UsersDepartment> UsersDepartments { get; set; }
}
public partial class UsersCompany
{
public int Id { get; set; }
public int UserId { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual User User { get; set; }
}
public partial class UsersDepartment
{
public int Id { get; set; }
public int UserId { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
public virtual User User { get; set; }
}
I have a method in which I pass the entity and attempt to map it to the db model. This is what I have right now after about 100 different attempts to get automapper to politely map my join tables...with no luck.
private DBUser ToDataModel(User user)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, DBUser>()
.ForMember(dest => dest.UsersDepartments, opt => opt.MapFrom(x => x.Departments));
cfg.CreateMap<User, DBUsersDepartment>()
.ForMember(x => x.User, y => y.MapFrom(z => z));
cfg.CreateMap<Department, DBUsersDepartment>();
});
IMapper mapper = config.CreateMapper();
return mapper.Map<DBUser>(user);
}
This is what I see after it 'maps' (notice no user information has been mapped):
Upvotes: 1
Views: 952
Reputation: 261
If you still want to know a possible solution to your problem for the future, here are the necessary changes to your code, to get it to work. While your DTO class User uses the direct related DTOs Department and Company, your DB class USerDb uses the many-many mappings UsersDepartment and UsersCompany.
public class User
{
...
public List<Department> Departments { get; set; }
public List<Company> Companies { get; set; }
}
public class DbUser
{
...
public List<UsersDepartment> UsersDepartments { get; set; }
public List<UsersCompany> UsersCompanies { get; set; }
}
cfg.CreateMap<UserDb, User>()
.ForMember(dest => dest.Departments, opt => opt.MapFrom(user => user.UsersDepartments.Select(userDepartment => userDepartment.Department)))
.ForMember(dest => dest.Companies, opt => opt.MapFrom(user => user.UsersCompanies.Select(userCompany => userCompany.Company)));
cfg.CreateMap<User,DbUser>()
.ForMember(dest => dest.UsersDepartments, opt => opt.MapFrom( user=>user.Departments.Select( department=> new UsersDepartment{UserId=user.Id, DepartmentId=department.Id}))))
.ForMember(dest => dest.UsersCompanies, opt => opt.MapFrom( user=>user.Companies.Select( company=> new UsersCompany{UserId=user.Id, CompanyId=company.Id}))));
I would also remove the additional Id from UsersDepartment and UsersCompany and use a combined key UsersId, DepartmentId or UseId,CompanyId instead.
Hope, that helps.
Upvotes: 1
Reputation: 95
For anyone interested. I ended up going a different route, where I update each collection separately, then update the base user class. ...now off to the next issue.
Upvotes: 0
Reputation: 3693
You are mapping from
public class Department{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { }
}
to
public partial class UsersDepartment{
public int Id { get; set; }
public int UserId { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }//No matching property in Department
public virtual User User { get; set; }//No matching property in Department
}
so it is normal that when maping Department
→UsersDepartment
public virtual User User { get; set; }
and
public virtual Department Department { get; set; }
will be null.
Moreover, you are creating a mapper each time you call private DBUser ToDataModel(User user)
, which is highly inefficient. Please consider, creating your mappers at the beginning of your application, for example if it is web application in global.asax
etc
Upvotes: 0