A complex Entity Framework / AutoMapper REST case - entity-framework

I am assigned the implementation of a REST GET with a complex DB model and somewhat complex output layout. Although I am a REST beginner, I have lost "rest" on this for 2 weeks spinning my wheels, and Google was of no help as well.
Here's a simplification of the existing DB I am given to work with:
Table group : {
Column id Guid
Column name string
Primary key: {id}
}
Table account
{
Column id Guid
Column name string
Primary key: {id}
}
Table groupGroupMembership
{
Column parentGroupId Guid
Column childGroupId Guid
Primary key: {parentGroupId, childGroupId}
}
Table accountGroupMembership
{
Column parentGroupId Guid
Column childAccountId Guid
Primary key: {parentGroupId, childAccountId}
}
So clearly you guessed it: There is a many-to-many relationship between parent a child groups. Hence a group can have many parent and child groups. Similarly, an account can have many parent groups.
The DB model I came up with in C# (in namespace DBAccess.Models.Tables):
public class Group
{
// properties
public Guid id { get; set; }
public string? name { get; set; }
// navigation properties
public List<GroupMemberAccount>? childAccounts { get; set; }
public List<GroupMemberGroup>? childGroups { get; set; }
public List<GroupMemberGroup>? parentGroups { get; set; }
}
public class Account
{
// properties
public Guid id { get; set; }
public string? name { get; set; }
// navigation properties
public List<GroupMemberAccount>? parentGroups { get; set; }
}
public class GroupMemberAccount
{
// properties
public Guid parentGroupId { get; set; }
public Guid childAccountId { get; set; }
//navigation properties
public Group? parentGroup { get; set; }
public Account? childAccount { get; set; }
static internal void OnModelCreating( EntityTypeBuilder<GroupMemberAccount> modelBuilder )
{
modelBuilder.HasKey(gma => new { gma.parentGroupId, gma.childAccountId });
modelBuilder
.HasOne(gma => gma.parentGroup)
.WithMany(g => g.childAccounts)
.HasForeignKey(gma => gma.parentGroupId);
modelBuilder
.HasOne(gma => gma.childAccount)
.WithMany(a => a.parentGroups)
.HasForeignKey(gma => gma.childAccountId);
}
}
public class GroupMemberGroup
{
// properties
public Guid parentGroupId { get; set; }
public Guid childGroupId { get; set; }
//navigation properties
public Group? parentGroup { get; set; }
public Group? childGroup { get; set; }
static internal void OnModelCreating(EntityTypeBuilder<GroupMemberGroup> modelBuilder)
{
modelBuilder.HasKey(gmg => new { gmg.parentGroupId, gmg.childGroupId });
modelBuilder
.HasOne(gmg => gmg.parentGroup)
.WithMany(g => g.childGroups)
.HasForeignKey(gmg => gmg.parentGroupId);
modelBuilder
.HasOne(gmg => gmg.childGroup)
.WithMany(g => g.parentGroups)
.HasForeignKey(gmg => gmg.childGroupId);
}
}
The corresponding DTO model I created:
public class Account
{
public Guid id { get; set; }
public string? name { get; set; }
public List<GroupMemberAccount>? parentGroups { get; set; }
}
public class AccountMappingProfile : AutoMapper.Profile
{
public AccountMappingProfile()
{
CreateMap<DBAccess.Models.Tables.Account, Account>();
}
}
public class Group
{
public Guid id { get; set; }
public string? Name { get; set; }
public GroupChildren children { get; set; } = null!;
};
public class GroupChildren
{
public List<GroupMemberAccount>? childAccounts { get; set; } = null!;
public List<GroupMemberGroup>? childGroups { get; set; } = null!;
}
public class GroupMemberAccount
{
public Guid parentGroupId { get; set; }
public Guid childAccountId { get; set; }
//public Group? parentgroup { get; set; } // commented out because no need to output in a GET request
public Account? childAccount { get; set; }
}
public class GroupMemberGroup
{
public Guid parentGroupid { get; set; }
public Guid childGroupId { get; set; }
//public Group? parentGroup { get; set; }; // commented out because no need to output in a GET request
public Group? childGroup { get; set; };
}
What you need to spot here is the difference in classes Group between the DB and DTO models.
In the DB model, Group has 3 lists: childAccounts, childGroups and parentGroups.
In the DTO model, Group has 1 node children of type GroupChildren which is a class that contains 2 of those 3 lists.
Hence an additional difficulty when it comes to design the mapping. That difference is intentional because it matches the following desired output for an endpoint such as: GET .../api/rest/group({some group guid}) is something like:
{
"id": "some group guid",
"name": "some group name",
"children": {
"childAccounts":{
"account":{ "name": "some account name 1"}
"account":{ "name": "some account name 2"}
...
}
"childFroups":{
"group":{ "name": "some group name 1"}
"group":{ "name": "some group name 2"}
...
}
},
}
obtained from following typical controller code:
[HttpGet("Groups({key})")]
[ApiConventionMethod(typeof(ApiConventions),
nameof(ApiConventions.GetWithKey))]
public async Task<ActionResult<Group>> Get(Guid key, ODataQueryOptions<Group> options)
{
var g = await (await context.Group.Include(g => g.childAccounts)
.Include(g => g.childGroups)
.Where(g => g.id == key)
.GetQueryAsync(mapper, options) // note the mapper here is the mapping defined below
).FirstOrDefaultAsync();
if (g is null)
{
return ResourceNotFound();
}
return Ok(g);
}
So here's the missing part to all this. Unless there are major errors in all of the above, I have a very strong intuition that it is the mapping that is failing to get me the requested output above.
public class GroupMappingProfile : AutoMapper.Profile
{
public GroupMappingProfile()
{
// the rather straightforward.
CreateMap<DBAccess.Models.Tables.GroupMemberAccount, GroupMemberAccount>();
CreateMap<DBAccess.Models.Tables.GroupMemberGroup, GroupMemberGroup>();
//Attempt 1: the not so straightforward. An explicit exhaustive mapping of everything, down to every single primitive type
CreateMap<DBAccess.Models.Tables.Group, Group>()
.ForMember(g => g.children, opts => opts.MapFrom(src => new GroupMembers
{
childAccounts = src.childAccounts!.Select(x => new GroupMemberAccount { parentGroupId = x.parentGroupId,
childAccountId = x.childAccountId,
childAccount = new Account { id = x.childAccount!.id,
name = x.childAccount!.name
}
}
).ToList(),
//childGroups = src.childGroups!.Select(x => new GroupMemberGroup(x)).ToList(),
childGroups = src.childGroups!.Select(x => new GroupMemberGroup { parentGroupId = x.parentGroupId,
childGroupId = x.childGroupId,
childGroup = new Group { id = x.childGroup!.id,
name = x.childGroup!.name
}
}
).ToList(),
}));
//Attempt 2: mapper injection
IMapper mapper = null!;
CreateMap<DBAccess.Models.Tables.Group, Group>()
.BeforeMap((_, _, context) => mapper = (IMapper)context.Items["mapper"]) //ADDING THIS LINE CAUSES ALL QUERIES TO LOOK FOR A NON EXISTENT Group.Groupid column
.ForMember(g => g.children, opts => opts.MapFrom(src => new GroupMembers
{
childAccounts = mapper.Map<List<DBAccess.Models.Tables.GroupMemberAccount>, List<GroupMemberAccount>>(src.childAccounts!),
childGroups = mapper.Map<List<DBAccess.Models.Tables.GroupMemberGroup>, List<GroupMemberGroup>>(src.childGroups!)
}))
}
}
Attempt1 will yield:
{
"id": "some guid",
"name": "some name"
"children": {}
}
even though the generated SQL does fetch all the required data to fill "children"
Attempt2 (mapper injection) is a technique I was suggested and have no clue how it is supposed to work. From what I gather, the mapping functions creates a few maps for some basic types while it uses its "future" self to create the remaining mappings, whenever it will be invoked in the future. Looks somehow like a one-time recursion.
However, it crashes as the generated SQL will look for a non-existent view column group.Groupid
SELECT [t].[id], [t].[name],
[g0].[parentGroupId], [g0].[childAccountId],
[g1].[parentGroupId], [g1].[childGroupId], [g1].[Groupid] -- where does [g1].[Groupid] come from??
FROM (
SELECT TOP(1) [g].[id], [g].[name]
FROM [HID_Rest].[group] AS [g]
WHERE [g].[id] = #__key_0
) AS [t]
LEFT JOIN [HID_Rest].[groupMemberAccount] AS [g0] ON [t].[id] = [g0].[parentGroupId]
LEFT JOIN [HID_Rest].[groupMemberGroup] AS [g1] ON [t].[id] = [g1].[parentGroupId]
ORDER BY ...
So regardless of the mapping profile I experimented with, what is the right mapping profile I need (and what ever else) to get the expected JSON output above? Or is this desired JSON structure possible at all?

After further work, I have figured that there was nothing wrong with my models and mapping. There's still something wrong though as the output to my GET requests is still incomplete. Here's the current new issue I need to deal with to solve this problem:
Issue with REST controller function Microsoft.AspNetCore.Mvc.ControllerBase.OK()?

Related

Configuring multiple one-to-many relations between same entities when the relations propagate deeper

I have 6 classes where the first class Money, goes deep 5 levels with objects. Whatever I try I cannot get this representation, so I hope someone would be kind to offer some help. At least for first 2,3 levels then I can continue.
public class Money
{
public Money()
{
Dollars = new HashSet<MoneyDetails>();
Pesos = new HashSet<MoneyDetails>();
Pounds = new HashSet<MoneyDetails>();
}
public int Id { get; set; }
public virtual ICollection<MoneyDetails> Dollars { get; }
public virtual ICollection<MoneyDetails> Pesos { get; }
public virtual ICollection<MoneyDetails> Pounds { get; }
public string Note { get; }
}
public class MoneyDetails
{
public MoneyDetails()
{
Valuations = new HashSet<Valuations>();
}
public int Id { get; set; }
public string Description { get; set; }
public double Value { get; set; }
public virtual ICollection<Valuation> Valuations { get; set; }
}
public class Valuations
{
public Valuations ()
{
Lows = new HashSet<Deep>();
Highs = new HashSet<Deep>();
}
public int Id { get; set; }
public string Sum { get; set; }
public virtual ICollection<Deep> Lows { get; set; }
public virtual ICollection<Deep> Highs { get; set; }
}
public class Deep
{
public Deep()
{
Shallows = new HashSet<Shallow>();
}
public int Id { get; set; }
public object Data { get; set; }
public virtual ICollection<Shallow> Shallows { get; set; }
}
EDIT :
I'm using Entity Framework Core.
Following is the configuration I tried myself.
You can see below how I started, I just don't know how to go deeper into objects and make relationships between them so they are connected.
public void Configure(EntityTypeBuilder<Money> builder)
{
builder.ToTable("Money");
builder.HasKey(e => e.Id);
builder.HasMany(s => s.Dollars)
.WithOne(ad => ad.Money)
.HasForeignKey(i => i.MoneyId);
builder.HasMany(s => s.Pesos)
.WithOne(ad => ad.Money)
.HasForeignKey(i => i.MoneyId);
builder.HasMany(s => s.Pounds)
.WithOne(ad => ad.Money)
.HasForeignKey(i => i.MoneyId);
}
You are using -
.WithOne(ad => ad.Money)
.HasForeignKey(i => i.MoneyId);
in your configuration code, but you don't have a Money navigation property or a MoneyId foreign-key property in MoneyDetails.
Since you are not using navigation and foreign-key properties in any of your entity models, I'd suggest not to configure the relations manually. Configure other properties in the Configure method if you need, but do not configure the relations. That way, EF will automatically create nullable foreign-keys in your tables and use them as Shadow Property.
EDIT - A better solution for you :
Even though the suggestion above will create all your tables with all the relations, I'm confused about how you plan to use those relations. For example, in the Money entity model Dollars, Pesos, Pounds are all collections of MoneyDetails. Therefore, all the following queries -
var money = myDbContext.Money.Include(p=> p.Dollars).FirstOrDefault(p=> p.Id == someId);
var money = myDbContext.Money.Include(p=> p.Pesos).FirstOrDefault(p=> p.Id == someId);
var money = myDbContext.Money.Include(p=> p.Pounds).FirstOrDefault(p=> p.Id == someId);
will give you the same result - the Money with the specified Id, with a list of all related MoneyDetails. So, there's no point of having three collection properties and three different relations.
Try the following approach to filter related data (you need EF Core 5.0) -
Create enum to identify the entity type -
public enum MoneyDetailsType { Dollar = 1, Peso = 2, Pound = 3 }
public enum DeepType { High = 1, Low = 2 }
Modify your entity models like -
public class Money
{
public Money()
{
MoneyDetails = new HashSet<MoneyDetails>();
}
public int Id { get; set; }
public string Note { get; }
public virtual ICollection<MoneyDetails> MoneyDetails { get; set; }
}
public class MoneyDetails
{
public MoneyDetails()
{
Valuations = new HashSet<Valuations>();
}
public int Id { get; set; }
public string Description { get; set; }
public double Value { get; set; }
public MoneyDetailsType Type { get; set; } // added - type
public int MoneyId { get; set; } // added - foreign-key
public Money Money { get; set; } // added - navigation property (optional)
public virtual ICollection<Valuations> Valuations { get; set; }
}
public class Valuations
{
public Valuations ()
{
Deeps = new HashSet<Deep>();
}
public int Id { get; set; }
public string Sum { get; set; }
public int MoneyDetailsId { get; set; } // added - foreign-key
public MoneyDetails MoneyDetails { get; set; } // added - navigation property (optional)
public virtual ICollection<Deep> Deeps { get; set; }
}
public class Deep
{
public int Id { get; set; }
public string Data { get; set; }
public DeepType Type { get; set; } // added - type
public int ValuationsId { get; set; } // added - foreign-key
public Valuations Valuations { get; set; } // added - navigation property (optional)
}
Notice, in Deep entity, currently you have Data as of type object which is not allowed. You have to change it to some primitive type. I'm using it as string. I have also omitted the Shallows property since you haven't added the Shallow model.
Your configuration methods should look like -
public void Configure(EntityTypeBuilder<Money> builder)
{
builder.ToTable("Money");
builder.HasMany(p => p.MoneyDetails)
.WithOne(p => p.Money)
.HasForeignKey(p => p.MoneyId);
}
public void Configure(EntityTypeBuilder<MoneyDetails> builder)
{
builder.ToTable("MoneyDetails");
builder.Property(p => p.Type).IsRequired(true).HasConversion(new EnumToStringConverter<MoneyDetailsType>());
builder.HasMany(p => p.Valuations)
.WithOne(p => p.MoneyDetails)
.HasForeignKey(p => p.MoneyDetailsId);
}
public void Configure(EntityTypeBuilder<Valuations> builder)
{
builder.ToTable("Valuations");
builder.HasMany(p => p.Deeps)
.WithOne(p => p.Valuations)
.HasForeignKey(p => p.ValuationsId);
}
public void Configure(EntityTypeBuilder<Deep> builder)
{
builder.ToTable("Deep");
builder.Property(p => p.Type).IsRequired(true).HasConversion(new EnumToStringConverter<DeepType>());
}
If you don't want to include the navigation properties in your entity models, then you can just keep the .WithOne() method empty in your configuration, like -
builder.HasMany(p => p.MoneyDetails)
.WithOne()
.HasForeignKey(p => p.MoneyId);
Now you can query like -
var money = myDbContext.Money
.Include(p=> p.MoneyDetails.Where(r=> r.Type == MoneyDetailsType.Dollar))
.FirstOrDefault(p=> p.Id == someId);
and it will give you the Money with a list of only Dollar type MoneyDetails.

Error when creating mappings with AutoMapper

I'm trying to map objects with AutoMapper. I've created the HTTP POST controller method, which should create the new Part object to database. It should add data to both entities, Part and PartAvailabilites. Database is already existing and is scaffolded by EF Core. The error I'm receiving is:
AutoMapper created this type map for you, but your types cannot be mapped using the current configuration Part -> PartDto (Destination member list)PartManagement.Entities.Part -> PartManagement.Dto.PartDto (Unmapped properties:Balance)"
Does anyone know what could be the problem with this mapping? I tried to do the mapping in several ways but none of them is working.
Here is my mapping:
CreateMap<PartDto, PartEntity>()
.ForMember(dest => dest.FkPartAvailability,
opts => opts.MapFrom(src => new PartAvailabilities
{
Balance = src.Balance
}));
Example JSON request:
{
"name": "testPart",
"catalogNumber": 12345,
"balance": 10
}
Here are my entity classes:
public class Part
{
public long Id { get; set; }
public int PartNumber { get; set; }
public string Name { get; set; }
public PartAvailabilities FkPartAvailability { get; set; }
}
public class PartAvailabilities
{
public PartAvailabilities()
{
Parts = new HashSet<Part>();
}
public long Id { get; set; }
public decimal Balance { get; set; }
public ICollection<Part> Parts { get; set; }
}
public class PartDto
{
public string Name { get; set; }
public int PartNumber { get; set; }
public decimal Balance { get; set; }
}
This is Create method in the ManagementService class:
public async Task<PartDto> Create(PartDto request)
{
var part = _mapper.Map<PartDto, PartEntity>(request);
var createdPart = partRepository.Add(part);
await partRepository.UnitOfWork.SaveChangesAsync();
return _mapper.Map<PartDto>(createdPart);
}
And here is HttpPost method from controller:
[HttpPost]
public async Task<IActionResult> Part_Create([FromBody] PartDto request)
{
PartDto createdPart;
try
{
if (request != null)
{
createdPart = await _partManagementService.Create(request);
return Ok(request);
}
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
return Ok(new string[] { "Part created" });
}
The message is trying to tell you that there is no map between Part and PartDto. AM will create a map for you, but that map is not valid, because PartDto.Balance cannot be mapped. So you have to create the map and tell AM how to map Balance. Things might be easier to understand if you set CreateMissingTypeMaps to false.

Entity Framework Core - issue with loading related entities that also contain related entities

I am using Entity Framework Core following Chris Sakell's blog here.
He uses generics to manage his repositories and also a base repository that he uses for all the other repositories.
Part of the base repository has the the following code for the retrieval of a single entity that also downloads related entities using the includeProperties option. Here is the generic code for a retrieving a single item.
public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = _context.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query.Where(predicate).FirstOrDefault();
}
I am using it on a client table that has many jobs attached to it.
This is how I structured my code.
public ClientDetailsViewModel GetClientDetails(int id)
{
Client _client = _clientRepository
.GetSingle(c => c.Id == id, c => c.Creator, c => c.Jobs, c => c.State);
if(_client != null)
{
ClientDetailsViewModel _clientDetailsVM = mapClientDetailsToVM(_client);
return _clientDetailsVM;
}
else
{
return null;
}
}
The line:
.GetSingle(c => c.Id == id, c => c.Creator, c => c.Jobs, c => c.State);
successfully retrieves values for creator state and job.
However, nothing is retrieved for those related entities associated with the "jobs".
In particuar, JobVisits is a collection of visits to jobs.
For completeness I am adding the "job" and "jobvisit" entities below
public class Job : IEntityBase
{
public int Id { get; set; }
public int? ClientId { get; set; }
public Client Client { get; set; }
public int? JobVisitId { get; set; }
public ICollection<JobVisit> JobVisits { get; set; }
public int? JobTypeId { get; set; }
public JobType JobType { get; set; }
public int? WarrantyStatusId { get; set; }
public WarrantyStatus WarrantyStatus { get; set; }
public int? StatusId { get; set; }
public Status Status { get; set; }
public int? BrandId { get; set; }
public Brand Brand { get; set; }
public int CreatorId { get; set; }
public User Creator { get; set; }
....
}
public class JobVisit : IEntityBase
{
...
public int? JobId { get; set; }
public Job Job { get; set; }
public int? JobVisitTypeId { get; set; }
public JobVisitType VisitType { get; set; }
}
My question is, how do I modify the repository code above and my GetSingle use so that I can also load the related enitities JobVisit collection and the other related single entities Brand and JobType?
It is intended that navigation properties are not necessary retrieved for associated with the "jobs". That is why some properties are null. By default the .Include(property); goes only 1-level deep and that is a good thing. It prevents your query from fetching all the data of your database.
If you want to include multiple levels, you should use .ThenInclude(property) after .Include(property). From the documentation:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList();
}
My advice is that your method public T GetSingle(...) is nice and I would not change it in order to include deeper levels. Instead of that, you can simply use explicit loading. From the documentation:
using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1);
context.Entry(blog)
.Collection(b => b.Posts)
.Load();
context.Entry(blog)
.Reference(b => b.Owner)
.Load();
}

Entity Framework Core (EF 7) many-to-many results always null

I have followed the instructions for the workaround for many-to-many described in Issue #1368 and the Docs Here... but when I try to navigate, it always returns null.
My Models:
public class Organization
{
public Guid OrganizationID { get; set; }
//...
public ICollection<OrganizationSubscriptionPlan> OrganizationSubscriptionPlans { get; set; }
}
public class SubscriptionPlan
{
public int SubscriptionPlanID { get; set; }
//...
public ICollection<OrganizationSubscriptionPlan> OrganizationSubscriptionPlans { get; set; }
public class OrganizationSubscriptionPlan
{
[ForeignKey("Organization")]
public Guid OrganizationID { get; set; }
public Organization Organization { get; set; }
[ForeignKey("SubscriptionPlan")]
public int SubscriptionPlanID { get; set; }
public SubscriptionPlan SubscriptionPlan { get; set; }
}
ApplicationDbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OrganizationSubscriptionPlan>().HasKey(x => new { x.OrganizationID, x.SubscriptionPlanID });
builder.Entity<OrganizationSubscriptionPlan>().HasOne(x => x.Organization).WithMany(x => x.OrganizationSubscriptionPlans).HasForeignKey(x => x.OrganizationID);
builder.Entity<OrganizationSubscriptionPlan>().HasOne(x => x.SubscriptionPlan).WithMany(x => x.OrganizationSubscriptionPlans).HasForeignKey(x => x.SubscriptionPlanID);
}
And my Query:
var organizations = _context.Organizations
.Include(o => o.OrganizationSubscriptionPlans);
foreach (var organization in organizations)
{
//....
var subscriptions = organization.OrganizationSubscriptionPlans
.Select(s => s.SubscriptionPlan);
// ^^^^^^^^^^^ why is subscriptions always null?
}
The "organizations" query returns the results as expected, including the list of OrganizationSubscriptionPlans within each one, but when I try to navigate to them in the foreach loop the "subscriptions" query returns null every time. What am I doing wrong?
Turns out it's a Lazy Loading issue. You have to "Include" the joining entity and then "ThenInclude" the other entity.
var organizations = _context.Organizations
.Include(o => o.OrganizationSubscriptionPlans)
.ThenInclude(s => s.SubscriptionPlan);
ForeignKey attr is to decorate reference properties to indicate them what primitive property hold the FK value.
public class OrganizationSubscriptionPlan
{
public Guid OrganizationID { get; set; }
[ForeignKey("OrganizationID")]
public Organization Organization { get; set; }
public int SubscriptionPlanID { get; set; }
[ForeignKey("SubscriptionPlanID")]
public SubscriptionPlan SubscriptionPlan { get; set; }
}

How to deal with relational entity mapping in AutoMapper?

Our Entity model has navigation properties:
public class Course
{
public Guid Id { get; set; }
...
public Guid InstructorId { get; set; }
public virtual Instructor Instructor { get; set; }
public virtual ICollection<Instructor> Coinstructors { get; set; }
}
That is, a course have one instructor and multiple coinstructors.
My view model has the id's of those instructors.
public class CourseCreateModel
{
...
public InstructorModel Instructor { get; set; }
public IEnumerable<InstructorModel> Coinstructors { get; set; }
}
The InstructorModel contains the Id:
public class InstructorModel
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
To make the data transfer from our DTO CourseCreateModel into the domain model Course, we can map the instructor easily because there is a InstructorId in the domain model:
Mapper.CreateMap<CourseCreateModel, Course>()
.ForMember(dest => dest.InstructorId, opts => opts.MapFrom(src => src.Instructor.Id))
...;
But how to map Coinstructors? We can get an array of coinstructor's id. But how to do the mapping?
I believe you have two options:
Option nr. 1 :
.ForMember(c=>c.Coinstructors,op=>op.MapFrom(v=>v.Coinstructorids.Select(c=>new Instructor(){Id=c})))
where Coinstructorids = List<int>();
Option nr. 2:
Create an custom resolver:
public class CustomConvert : ValueResolver<IList<int>, IList<Instructor>>
{
protected override string ResolveCore(IList<int> source)
{
string result = new List<Instructor>();
foreach (var item in source)
{
result.Add(new Instructor() {Id = item});
}
return result;
}
}
and the use it :
.ForMember(c => c.Coinstructors, op => op.ResolveUsing<CustomConvert>().FromMember(c => c.Coinstructorids));
where Coinstructorids = List<int>();