Map IList to ICollection through AutoMapper - automapper

public class Order
{
public int OrderId { get; set; }
public string OrderCode { get; set; }
public IList<OrderItem> OrderItems { get; set; }
}
public class OrderDTO
{
public string OrderId { get; set; }
public string IdentifiableCode { get; set; }
public decimal TotalCost { get; set; }
public ICollection OrderItems { get; set; }
}
public class OrderItem
{
public int OrderItemId { get; set; }
public int ItemCount { get; set; }
public Order Order { get; set; }
}
public class OrderItemDTO
{
public int OrderItemId { get; set; }
public int ItemCount { get; set; }
public OrderDTO Order { get; set; }
}
In this scenario how can i map IList of OrderItems to ICollection. I tried to use resolver but ended up in recursive since OrderItem has Order reference.
Any ideas?

First off, I added a few tweaks to your classes. One was to add constructors that initialize the collections so I could add to them in my test. Second, I don't see why you'd want the OrderDTO.OrderItems to be a loosely-typed ICollection. If you do that, Automapper just assigns the IList to the ICollection (since IList implements ICollection). If you define it as IList, Automapper will see that it knows how to convert from OrderItem to OrderItemDTO and will map each member of the collection.
I also had to add some Ignore()'s since OrderItemDTO doesn't contain IdentifiableCode nor TotalCost (not sure what you wanted to do with those). Finally, the parent/child mapping (OrderItemDTO having a reference to its parent) can be done by a simple foreach loop in the AfterMap(). So here's the mapping I came up with:
Mapper.CreateMap<Order, OrderDTO>()
.ForMember(d => d.IdentifiableCode, o => o.Ignore())
.ForMember(d => d.TotalCost, o => o.Ignore())
.AfterMap((s, d) =>
{
foreach (var l in d.OrderItems)
{
l.Order = d;
}
});
Mapper.CreateMap<OrderItem, OrderItemDTO>();
Mapper.AssertConfigurationIsValid();
And here's the test I used to check things out:
var order = new Order
{
OrderCode = "AAA",
OrderId = 22,
OrderItems = new List<OrderItem>(),
};
order.OrderItems.Add(new OrderItem { ItemCount = 2, Order = order, OrderItemId = 33 });
order.OrderItems.Add(new OrderItem { ItemCount = 1, Order = order, OrderItemId = 99 });
var mapped = Mapper.Map<Order, OrderDTO>(order);

Related

AutoMapper One-To-Many Filter and ProjectTo

I have the below two Entities (one-to-many)
public class ApplicationCode
{
public Guid ApplicationId { get; set; }
public string? ApplicationAcrynom { get; set; }
public int ApplicationIndex { get; set; }
public IList<ApplicationCodeTranslation> ApplicationCodeTranslations { get; private set; } = new List<ApplicationCodeTranslation>();
}
public class ApplicationCodeTranslation
{
public Guid ApplicationCodeTranslationId { get; set; }
public Guid ApplicationId { get; set; }
public Guid LanguageId { get; set; }
public string? ApplicationDescription { get; set; }
public ApplicationCode ApplicationCode { get; set; } = null!;
}
The goal is to populate the below Dto where I need to filter one language from the child list (IList ApplicationCodeTranslations) in the ApplicationCode entity to get the description value
public class ApplicationCodeDto : IMapFrom<ApplicationCode>
{
public Guid ApplicationId { get; set; }
public string? ApplicationAcrynom { get; set; }
public string? ApplicationDescription { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<ApplicationCode, ApplicationCodeDto>();
profile.CreateMap<ApplicationCodeTranslation, ApplicationCodeDto>();
}
}
I tried below, but it does not give the intended result.
It omits the ApplicationDescription (gives null), the whole .Include line is not translated to SQL at all.
var test1 = await _context.ApplicationCodes
.Include(e => e.ApplicationCodeTranslations.Where(l => l.LanguageId == _languageId))
.ProjectTo<ApplicationCodeDto>(_mapper.ConfigurationProvider)
.ToListAsync();
The other option I tried is
var test2 = await _context.ApplicationCodes
.SelectMany(e => e.ApplicationCodeTranslations)
.Where(l => l.LanguageId == _languageId)
.ProjectTo<ApplicationCodeDto>(_mapper.ConfigurationProvider)
.ToListAsync();
Which omits the properties of the CodeApplication and brings only the child list properties, as SelectMany only brings back the props of the child.
The only way I managed to make it work is below:
var test3 = await _context.ApplicationCodes
.SelectMany(e => e.ApplicationCodeTranslations.Where(l => l.LanguageId == _languageId),
(a,t) => new ApplicationCodeDto{
ApplicationId = a.ApplicationId,
ApplicationAcrynom = a.ApplicationAcrynom,
ApplicationDescription = t.ApplicationDescription,
})
.ToListAsync();
How can AutoMapper (v.11) help in this scenario to avoid mapping the DTO props manually?
Does ProjectTo work here?

Selecting OrmLite new object from joined table for insertion

I have 3 entities:
[CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
public class WatchedUrlRecord
{
[AutoIncrement]
public long Id { get; set; }
public string Url { get; set; }
public string Provider { get; set; }
public string DomainKey { get; set; }
public WatchedUrlScanStatus WatchedUrlScanStatus { get; set; }
public bool NoFollow { get; set; }
public HttpStatusCode HttpStatusCode { get; set; }
public DateTime? LastScanTime { get; set; }
public WatchedUrlScanResult LastScanData { get; set; }
public string Anchors { get; set; }
public int? OutboundLinks { get; set; }
[ForeignKey(typeof(TargetDomainRecord), OnDelete = "CASCADE")]
public long TargetDomainRecordId { get; set; }
[ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
public long UserAuthCustomId { get; set; }
}
[CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
public class WatchedUrlQueue
{
[PrimaryKey]
public long WatchedUrlRecordId { get; set; }
[Index]
public string Url { get; set; }
[Index]
public string DomainKey { get; set; }
[Index]
public long TargetDomainRecordId { get; set; }
public string TargetDomainKey { get; set; }
[Index]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
public int Tries { get; set; }
[Index]
public DateTime? DeferUntil { get; set; }
[Index]
public long UserAuthCustomId { get; set; }
[Index]
public bool FirstScan { get; set; }
}
[CompositeIndex(nameof(Url), nameof(UserAuthCustomId), Unique = true)]
public class TargetDomainRecord
{
[AutoIncrement]
public long Id { get; set; }
public string Url { get; set; }
public string DomainKey { get; set; }
public DateTime CreateDate { get; set; } = DateTime.Now;
public DateTime? DeleteDate { get; set; }
public bool IsDeleted { get; set; }
public bool Active { get; set; } = true;
public DomainType DomainType { get; set; }
[ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
public long UserAuthCustomId { get; set; }
}
I am trying to insert queue objects based on IDs of WatchedUrlRecords so I came up with this query:
var q = db.From<WatchedUrlRecord>()
.Where(x => Sql.In(x.Id, ids))
.Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
.Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new WatchedUrlQueue()
{
UserAuthCustomId = w.UserAuthCustomId,
DomainKey = w.DomainKey,
CreateDate = DateTime.UtcNow,
DeferUntil = null,
FirstScan = firstScan,
TargetDomainKey = t.DomainKey,
Tries = 0,
TargetDomainRecordId = w.TargetDomainRecordId,
Url = w.Url,
WatchedUrlRecordId = w.Id
});
var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd => dbCmd.OnConflictIgnore());
This doesn't work and gives error:
variable 'w' of type 'Project.ServiceModel.WatchedUrl.Entities.WatchedUrlRecord' referenced from scope '', but it is not defined
If I try anonymous object like new {} instead of new WatchedUrlQueue then InsertIntoSelect() throws error:
'watched_url_record"."user_auth_custom_id' is not a property of 'WatchedUrlQueue'
I have looked in documentation and can see SelectMulti() method but I don't think that is suitable as it will involve me creating a tuple list to combine into the new object. The passed list can be quite large so I just want to send the correct SQL statement to PostgreSQL which would be along lines of:
insert into watched_url_queue (watched_url_record_id, url, domain_key, target_domain_record_id, target_domain_key, create_date, tries, defer_until, user_auth_custom_id)
select wur.id watched_url_record_id,
wur.url url,
wur.domain_key,
wur.target_domain_record_id,
tdr.domain_key,
'{DateTime.UtcNow:MM/dd/yyyy H:mm:ss zzz}' create_date,
0 tries,
null defer_until,
wur.user_auth_custom_id
from watched_url_record wur
join target_domain_record tdr on wur.target_domain_record_id = tdr.id
where wur.id in (323,3213123,312312,356456)
on conflict do nothing ;
I currently have a lot of similar type queries in my app and it is causing extra work maintaining them, would be really nice to be able to have them use fluent api without reducing performance. Is this possible?
Custom select expression can't be a typed projection (i.e. x => new MyType { ... }), i.e. you'd need to use an anonymous type expression (i.e. new { ... }) which captures your query's Custom SELECT Projection Expression.
You'll also need to put your JOIN expressions directly after FROM (as done in SQL) which tells OrmLite it needs to fully qualify subsequent column expressions like Id which would otherwise be ambiguous.
I've resolved an issue with field resolution of custom select expressions in this commit where your query should now work as expected:
var q = db.From<WatchedUrlRecord>()
.Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
.Where(x => Sql.In(x.Id, ids))
.Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new {
UserAuthCustomId = w.UserAuthCustomId,
DomainKey = w.DomainKey,
CreateDate = DateTime.UtcNow,
DeferUntil = (DateTime?) null,
FirstScan = firstScan,
TargetDomainKey = t.DomainKey,
Tries = 0,
TargetDomainRecordId = w.TargetDomainRecordId,
Url = w.Url,
WatchedUrlRecordId = w.Id
});
var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd=>dbCmd.OnConflictIgnore());
This change is available from v5.10.5 that's now available on MyGet.

Automapper Object with inside list of object to primitive mapping

I'm trying to create map for automapper to let me map those entity
Entities
public class Entity
{
...
public List<NavigationEntity> Navs { get; set; }
}
public class NavigationEntity
{
public int Id { get; set; }
}
DTO that need to be create with entities
public class EntityDto
{
...
public List<int> NavIds { get; set; }
}
This doesnt seem's to do the job! What could do the job ?
CreateMap<Entity, EntityDto>().ReverseMap();
CreateMap<NavigationEntity, int>().ConstructUsing(x => x.Id);
EDIT
Tried to add
CreateMap< List < SystemsTags >, List< int >>();
but still it doesnt map
First of all, you should rename public List<NavigationEntity> Navs { get; set; } and public List<int> NavIds { get; set; } to the same name. If it is still not working try to also change ConstructUsing to ConvertUsing. And if you need the reverseMap of Entity to EntityDTO you should also add
CreateMap<int, NavigationEntity>().ConvertUsing(x => new NavigationEntity { Id = x });
final code
public class Entity
{
...
public List<NavigationEntity> Navs { get; set; }
}
public class NavigationEntity
{
public int Id { get; set; }
}
public class EntityDto
{
...
public List<int> Navs { get; set; }
}
...
CreateMap<Entity, EntityDto>().ReverseMap();
CreateMap<NavigationEntity, int>().ConvertUsing(x => x.Id);
CreateMap<int, NavigationEntity>().ConvertUsing(x => new NavigationEntity { Id = x });

Automapper, Mapping one object member type to multiple concrete type

I have this Party class which contains an object data type coming from a service. It can contain two different member types for the Item property.
public class Party
{
public string DMVID {get; set;}
public object Item { get; set; }
}
and this DTO
public class PartyDTO
{
public string DMVID {get; set;}
public BusinessDTO BusinessItem { get; set; }
public IndividualDTO IndividualItem { get; set; }
}
How can I map the output of the Item to BusinessItem or IndividualItem.
I know this one would not work. Mapper.CreateMap<Party, PartyDTO>();
I don't know if conditional mapping can solve this or a resolver like this one.
Hey maybe this will help you out! I tested it, but i am using AutoMapper just for two days!
Allright here are your noted classes!!!
public class Party
{
public string DMVID { get; set; }
public object Item { get; set; }
}
public class PartyDTO
{
public string DMVID { get; set; }
public BuisnessDTO BusinessItem { get; set; }
public IndividualDTO IndividualItem { get; set; }
}
public class BuisnessDTO
{
public int Number
{
get;
set;
}
}
public class IndividualDTO
{
public string Message
{
get;
set;
}
}
and here your is the MapperConfiguration for this current scenario!
// Edit There was no need here for some conditions
AutoMapper.Mapper.CreateMap<Party, PartyDTO>()
.ForMember(dto => dto.BusinessItem, map =>
map.MapFrom(party => party.Item as BuisnessDTO);
)
.ForMember(dto => dto.IndividualItem, map =>
map.MapFrom(party => party.Item as IndividualDTO);
);
// And this is another way to achive the mapping in this scenario
AutoMapper.Mapper.CreateMap<PartyDTO, Party>()
.ForMember(party => party.Item, map => map.MapFrom( dto => (dto.BusinessItem != null) ? (dto.BusinessItem as object) : (dto.IndividualItem as object)));
And i created this sample for it!
Party firstParty = new Party()
{
DMVID = "something",
Item = new BuisnessDTO()
{
Number = 1
}
};
Party secondParty = new Party()
{
DMVID = "something",
Item = new IndividualDTO()
{
Message = "message"
}
};
PartyDTO dtoWithBuisness = AutoMapper.Mapper.Map<PartyDTO>(firstParty);
PartyDTO dtoWithIndividual = AutoMapper.Mapper.Map < PartyDTO>(secondParty);
Party afterParty = AutoMapper.Mapper.Map<Party>(dtoWithBuisness);
afterParty = AutoMapper.Mapper.Map < Party>(dtoWithIndividual);
Of course there are other possibility, but I think thats exactly what you wanted.

Selecting child projection from parent

Here are three classes in my domain:
public class Quote : IEntity, IAggregateRoot {
public int QuoteId { get; set; }
public IEnumerable<Price> Prices { get; set; }
}
public class Price : IEntity {
public int PriceId { get; set; }
public Carrier Carrier { get; set; }
public decimal? Price { get; set; }
public Quote Quote { get; set; }
}
public class Carrier : IEntity, IAggregateRoot {
public int CarrierId { get; set; }
public string Name { get; set; }
}
I want to be able to select a projection based on the Prices in the Quote. The return type should be an IEnumerable<[anonymous object]>. I have to start the query from the Quote because it is the root domain object. Here is what I have so far:
session.Linq<Quote>()
.Expand("Prices")
.Where(q => q.QuoteId == 1)
.Select(q => {
//this is where I don't know what to do.
//Maybe somthing like this:
return q.Prices.Select(p => {
new { CustomerName = p.Customer.Name, Price = p.Price }
});
});
The mappings would be:
Quote.Prices > HasMany (one-to-many)
Price.Quote > References (many-to-one)
Price.Carrier > References (one-to-one)
I found my answer. I completely forgot about the SelectMany Linq expression. Here is my solution.
session.Linq<Quote>()
.Where(q => q.QuoteId == 1)
.SelectMany(q => q.Prices, (q, p) => new { CustomerName = p.Customer.Name });

Resources