automapper optimization mapping (model to view model -> get latest version) - c#

I am trying to optimize this part of code:
Mapper.CreateMap<Document, DocumentViewModel>()
.ForMember(g => g.Id, map => map.MapFrom(d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().Id))
.ForMember(g => g.IdRootDocument, map => map.MapFrom(d => d.Id))
.ForMember(g => g.certyficateType, map => map.MapFrom(d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().certyficateType))
I'm Using automapper, and I'm trying to optimize this part of code
In this part I'm trying mapping object from document to documentViewModel, in this complex model, source data always will be latest document version:
d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().myProportyX
Could anyone offer an example or suggestion as to how to approach optimization in this situation?

d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().myProporty
You are iterating quite a few times here, you may consider doing something like:
d.documentVersion.OrderByDescending(v => v.version).ThenByDescending(v => v.subVersion).First().myProperty
to reduce the number of iterations and only get the top version/subversion.

optimization mapping:
Mapper
.CreateMap<Document, DocumentViewModel>()
.ConvertUsing(doc =>
{
DocumentViewModel result = new DocumentViewModel();
DocumentVersion lastVersion = doc.documentVersion.Where(v => v.version == doc.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First();
Mapper.Map(lastVersion, result);
return result;
});

Related

Nhibernate QueryOver Project enum description to string

Let`s say we have the following query using Nhibernate:
var query = session.QueryOver<TenantOrder>(() => tenantOrderAlias)
.JoinAlias(() => tenantOrderAlias.Tenant, () => tenantAlias)
.JoinAlias(() => tenantAlias.Building, () => buildingAlias)
.WhereRestrictionOn(x => tenantOrderAlias.Id.OrderId).IsLike(order.Id);
query.Select(
Projections.Property(() => tenantAlias.Id).WithAlias(() => tenantDto.Id),
Projections.Property(() => tenantAlias.TenantNr).WithAlias(() => tenantDto.TenantNr),
Projections.Property(() => buildingAlias.BuildingNr).WithAlias(() => tenantDto.BuildingNr),
Projections.Property(() => tenantAlias.Floor).WithAlias(() => tenantDto.Floor),
Projections.Property(() => tenantOrderAlias.InstallationStatus).WithAlias(() => tenantDto.InstallationStatusName //? ));
We want to process InstallationStatus (type enum) from tenantOrderalias to InstallationStatusName (property of type string from tenantDto). The structure of the enum looks like below :
public enum TenantInstallationStatusEnum
{
[StringEnum("MS3_TenantInstallationStatus_TS0")]
TS0,//open
[StringEnum("MS3_TenantInstallationStatus_TS2")]
TS2,//abgelesen
}
We want to get the description of the enum and place it to tenantDto.InstallationStatusName. Is this possible using QueryOver or ICriteria ?
We cannot find any post about this issue.
In general, we can project only stuff which exists in a DB side or is passed as a "formula". So, we can convert the status into some conditional statement like this:
// this projection
Projections
.Property(() => tenantOrderAlias.InstallationStatus)
.WithAlias(() => tenantDto.InstallationStatusName //? ));
// could be converted into string values with this statement
Projections
.Conditional(
Restrictions.Where<TenantOrder>(to =>
to.InstallationStatus == TenantInstallationStatusEnum.TS0),
Projections.Constant("MS3_TenantInstallationStatus_TS0"),
Projections.Constant("MS3_TenantInstallationStatus_TS2")
).WithAlias(() => tenantOrderAlias.InstallationStatusName)
);
Conditional could be even nested... but at the end it could be a bit more challenging.
But, maybe more easier in fact, would be to process this conversion ex-post, in C#, on the app side...

MapFrom not always mapping properly

Here's a piece of simple code:
var map = Mapper.CreateMap<Contact, ContactDtoGrid>().
ForMember("executive_account", x => x.MapFrom(y => y.executive_account.Id)).
ForMember("executive_account_Name", x => x.MapFrom(y => y.executive_account.Name));
foreach (Contact c in allContacts)
{
string accountNumber = accounts.Where(x=>x.Key==c.executive_account.Id).SingleOrDefault().Value;
string protocolDescription = (protocolDefinitions.Where(x => x.ProtocolOptionSetValue == c.executive_protocol).Any()) ? protocolDefinitions.Where(x => x.ProtocolOptionSetValue == c.executive_protocol).SingleOrDefault().PortalDescription : string.Empty;
if (protocolDescription != string.Empty)
Console.Write(protocolDescription); //I do get an output so it's not always empty
map.ForMember("executive_account_Number", x => x.MapFrom(y => accountNumber));
map.ForMember("executive_protocol_desc", x => x.MapFrom(y => protocolDescription));
var contactDto = Mapper.Map<Contact, ContactDtoGrid>(c);
}
So all properties map properly, except for executive_protocol_desc which is always an empty string. Like I mentionned in the comment above, this is the case regardless whether protocolDescription is an empty string or not.
Am I doing something obviously wrong ?
Thanks.
Inside the method, you're not really doing a MapFrom, but setting a value. Since automapper is using nested closures, it's just easier to use a direct assignment:
...
var contactDto = Mapper.Map<Contact, ContactDtoGrid>(c);
contactDto.executive_protocol_desc = protocolDescription;
If you need to keep it in the mapper, you should be able to just put the protocol magic as the expression. This would allow you to pull the protocol description out of the foreach loop. Note: I also simplified the linq expression.
map.ForMember("executive_protocol_desc", opt => opt.MapFrom(src =>
protocolDefinitions.Any(pdef => pdef.ProtocolOptionSetValue == src.executive_protocol))
? protocolDefinitions.SingleOrDefault(pdef => pdef.ProtocolOptionSetValue == src.executive_protocol).PortalDescription
: string.Empty)
I took those lines out of the loop:
map.ForMember("executive_account_Number", x => x.MapFrom(y => accountNumber));
map.ForMember("executive_protocol_desc", x => x.MapFrom(y => protocolDescription));
And before the loop kicks in, I changed it to this:
map.ForMember(dest => dest.executive_protocol_desc, opt => opt.MapFrom(x => protocolDefinitions.Where(y => y.ProtocolOptionSetValue == x.executive_protocol.Value).SingleOrDefault().PortalDescription));
map.ForMember(dest => dest.executive_account_Number, opt => opt.MapFrom(x => accounts.Where(y => y.Key == x.executive_account.Id).SingleOrDefault().Value));
Now this maps properly.

How to partially project a child object with many fields in nHibernate

I have the following nHibernate query that select a course based on its course id and then return selected fields for the course object on the initial fetch, and the query executes with no issues.
MatchMode option = ...
CourseItem courseAlias = null;
TeacherItem teacherAlias = null;
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
.Select(c => c.Teacher).WithAlias(() => courseAlias.Teacher))
.TransformUsing(Transformers.AliasToBean<CourseItem>())
.List<CourseItem>();
I wanted to go a step further with the query to only return a partial teacher object, let's say i just wanted to return the ID and Name. So, I updated the projected list to as follows:
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
.Select(c => c.Teacher.ID).WithAlias(() => courseAlias.Teacher.ID)
.Select(c => c.Teacher.Name).WithAlias(() => courseAlias.Teacher.Name))
.TransformUsing(Transformers.AliasToBean<CourseItem>())
.List<CourseItem>();
The query doesn't work because nHibernate has no idea how to resovled based on Teacher.ID and Teacher.Name. Any thoughts on whether it's possible to NOT fetch the entire child object back to a parent object?
I've tried the following query and it works this is not my fully desired outcome
var query = session.QueryOver<CourseItem>(() => courseAlias)
.JoinAlias(() => courseAlias.Teacher, () => teacherAlias)
.Where(() => courseAlias.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(() => courseAlias.CourseID)
.Select(() => courseAlias.IsActive)
.Select(() => courseAlias.CourseDesc)
.Select(() => teacher.ID)
.Select(() => teacher.Name))
.List<object[]>();
I can query the right values but unable to transform it back correctly to the Course / teacher data type.
Any thoughts?
thanks!
We can indeed use custom transformer. There is one, which I am using for a really very very deep projections (inlcuding dynamic objects - 5.1.13. component, dynamic-component)
DeepTransformer<TEntity>
Take it (if needed adjust it) and your final query could be like this
// just the last lines are different
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
// the native WitAlias would not work, it uses expression
// to extract just the last property
//.Select(c => c.Teacher.ID).WithAlias(() => courseAlias.Teacher.ID)
//.Select(c => c.Teacher.Name).WithAlias(() => courseAlias.Teacher.Name))
// so we can use this way to pass the deep alias
.Select(Projections.Property(() => teacherAlias.ID).As("Teacher.ID"))
.Select(Projections.Property(() => teacherAlias.Name).As("Teacher.Name"))
// instead of this
// .TransformUsing(Transformers.AliasToBean<CourseItem>())
// use this
.TransformUsing(new DeepTransformer<CourseItem>())
And in case, that your aliases do match to property names, that transformer will built the object tree...

Automapper: How do I map Lazy<TFrom> to TTo?

I need to map a list of Lazy<TFrom> to a list of TTo. What I've done isn't working - I only get null values. Without the Lazy<> wrapper it works perfectly. What am I doing wrong?
// Map this list
// It's passed in as an argument and has all the data
IList<Lazy<Employee>> employees
public static IList<TTo> MapList(IList<Lazy<TFrom>> fromModel)
{
Mapper.CreateMap<Lazy<TFrom>, TTo>();// Is this needed?
return Mapper.Map<IList<Lazy<TFrom>>, IList<TTo>>(fromModel);
// This doesn't work
// return Mapper.Map<IList<TTo>>(fromModel.Select(x => x.Value));
}
// Maps the child classes
Mapper.CreateMap<Lazy<Employee>, EmployeeDTO>()
.ForMember(x => x.AccidentDTO, i => i.MapFrom(model => model.Value.GetAccident()))
.ForMember(x => x.CriticalIllnessDTO, i => i.MapFrom(model => model.Value.GetCriticalIllness()))
.ForMember(x => x.ValidationMessages, i => i.MapFrom(model => model.Value.ValidationMessages));
// Returns nulls !!!
var dataDTO = MyMapper<Lazy<Employee>, EmployeeDTO>.MapList(employees);
Here's what I've done to solve my problem. If someone has a better idea, please let me know and I'll mark it as the answer.
// Get the Values of the Lazy<Employee> items and use the result for mapping
var nonLazyEmployees = employees.Select(i => i.Value).ToList();
var dataDTO = MyMapper<Employee, EmployeeDTO>.MapList(nonLazyEmployees);
public static IList<TTo> MapList(IList<Lazy<TFrom>> fromModel)
{
Mapper.CreateMap<Lazy<TFrom>, TTo>();
return Mapper.Map<IList<Lazy<TFrom>>, IList<TTo>>(fromModel);
}
// Maps the child classes
Mapper.CreateMap<Employee, EmployeeDTO>()
.ForMember(x => x.AccidentDTO, i => i.MapFrom(model => model.GetAccident()))
.ForMember(x => x.CriticalIllnessDTO, i => i.MapFrom(model => model.GetCriticalIllness()))
.ForMember(x => x.ValidationMessages, i => i.MapFrom(model => model.ValidationMessages));

Loading all the children entities with entity framework

I have a data model like this
I would like to load all the related entities from a Reconciliation into a Reconciliation object.
For now the only way I could find to load all the related entites to a single Recon is in multiple Lists. But I want to load every related entities in a Reconciliation object. If possible in an elegant way.
Reconciliation recon = db.Reconciliations
.Where(r => r.ReconNum == 382485).First();
List<ReconciliationDetail> reconDetails = recon.ReconciliationDetails.ToList();
List<JrnlEntryDetail> jrnlDetails = reconDetails.Select(r => r.JrnlEntryDetail).ToList();
List<JrnlEntry> jrnl = jrnlDetails.Select(j => j.JrnlEntry).ToList();
List<ARInvoice> invoices = jrnl.SelectMany(j => j.ARInvoices).ToList();
List<ARInvoiceDetail> invoicesDetail = invoices
.SelectMany(i => i.ARInvoiceDetails).ToList();
List<ARCredMemo> credmemos = jrnl.SelectMany(j => j.ARCredMemoes).ToList();
List<ARCredMemoDetail> credmemosDetail = credmemos
.SelectMany(c => c.ARCredMemoDetails).ToList();
List<IncomingPay> incomingPays = jrnl.SelectMany(j => j.IncomingPays).ToList();
List<IncomingPayDetail> incomingPaysDetail = incomingPays
.SelectMany(i => i.IncomingPayDetails).ToList();
// ... and so on for outgoing pays, AP Invoices AP Cred Memo ...etc
I have also tried to load it with Include and Select but I get this exception :
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
And I don't get how I could load every childs of JrnlEntry using Include and Select
Reconciliation recon = db.Reconciliations
.Where(r => r.ReconNum == 382485)
.Include(r => r.ReconciliationDetails
.Select(d => d.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry)
.SelectMany(j => j.ARInvoices).SelectMany(i => i.ARInvoiceDetails))
Edit
Managed to do it this way too but it's not very beautiful :
Reconciliation recon = db.Reconciliations
.Where(r => r.ReconNum == 382485)
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.ARInvoices.Select(i => i.ARInvoiceDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.ARCredMemoes.Select(c => c.ARCredMemoDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.IncomingPays.Select(i => i.IncomingPayDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.OutgoingPays.Select(o => o.OutgoingPayDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.APInvoices.Select(o => o.APInvoiceDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.APCredMemoes.Select(o => o.APCredMemoDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.JrnlEntryDetails))
There are two ways to perform Eager Loading in Entity Framework:
ObjectQuery.Include Method
Explicit loading using either DbEntityEntry.Collection Method or DbEntityEntry.Reference Method along with their respective Load methods
There are also manners to write Raw SQL Queries against database:
DbSet.SqlQuery Method deals with entities
Database.SqlQuery Method deals with arbitrary types
Database.ExecuteSqlCommand Method for arbitrary DDL/DML
For the case, when you're attempting to load nearly entire database, it would be good idea to execute dedicated store procedure against it.
Try with just .Include(r => r.ReconciliationDetails) initially. Then add the .Select() statements one-by-one. At what point does the exception reappear? The .SelectMany() call looks a bit suspicious to me!
A second question that might help identify the problem... After you run the code that contains all the ToList() calls, is your recon entity complete? i.e. are all its navigation properties populated? This should be the case because of the automatic 'fixup' behavior of Entity Framework.
With EF, sometimes it is more efficient to load a complex object graph with several calls rather than chained Include() calls. Check the generated SQL and see what is most efficient in your case.
Not sure if it's to late but could you benefit from structuring your code such as
var acctName = "someName";
var detailList = _repository.Include(e => e.JrnlEntryDetail).Filter(c => c.JrnlEntryDetail.Any(e => e.AcctName == acctName)).Get().ToList();

Categories

Resources