Mapping in automapper based on a variable - c#

I am using Automapper for mapping my dtos. I have a tricky scenario where I want to map on condition.
if (countryCode == "")
CreateMap<src, dest>()
.ForMember(dest => dest.CountryOriginCode,
act => act.MapFrom(src =>
src.FirstOrDefault(k => k.Key == "CountryExportCode").Value))
else
CreateMap<src, dest>()
.ForMember(dest => dest.CountryOriginCode,
act => act.MapFrom(src =>
src.FirstOrDefault(k => k.Key == "CountryOriginCode").Value))
I have 2 requirements here.
I want to map it conditionally
How can I get the value of CountryCode which is in another object say CountryObject
Edit -
CreateMap<country, countryDto>()
.ForMember(dest => dest.CountryCode, act => act.MapFrom(src => src.CountryCode))
CreateMap<src, dest>()
.ForMember(
dest => dest.CountryOriginCode,
act => act.MapFrom(src => countryCode == CountryCode ? // Observer the CountryCode , I need it from above
src.FirstOrDefault(k => k.Key == "CountryExportCode").Value:
src.FirstOrDefault(k => k.Key == "CountryOriginCode").Value)
)

How about this
CreateMap<src, dest>()
.ForMember(
dest => dest.CountryOriginCode,
act => act.MapFrom(src => countryCode == "" ?
src.FirstOrDefault(k => k.Key == "CountryExportCode").Value:
src.FirstOrDefault(k => k.Key == "CountryOriginCode").Value)
)

Related

Automapper - conditional mapping on destination

Mapping:
.ForMember(dest => dest.DisplayName, opt =>
{
opt.PreCondition(location => location.Parent != null);
opt.MapFrom(src => src.Name + ", " + src.Parent.Name);
})
.ForMember(dest => dest.DisplayName, opt =>
{
opt.PreCondition((src, dest, context) => dest.DisplayName == null);
opt.MapFrom(src => src.Name);
})
Expected result:
If the first condition is met don't override the mapping.
What actually happens:
The second mapping is overriding the first mapping.
How can I solve this?
It doesn't work because you are overwriting previous mapping expressions calling another ForMember() for the same member, which is your case is DisplayName. Consider such case:
.ForMember(d => d.DisplayName, o => o.MapFrom(s => "1"))
.ForMember(d => d.DisplayName, o => o.MapFrom(s => "2"))
.ForMember(d => d.DisplayName, o => o.MapFrom(s => "3"));
Which value will be mapped to DisplayName?
3
So in your case, your first conditional mapping expression is overwriten by the second one. To make it work, join the conditional mapping into one mapping expression:
.ForMember(
dest => dest.DisplayName,
opts => opts.MapFrom((src, dest) =>
{
if (src.Parent != null)
{
return string.Join(", ", src.Name, src.Parent.Name);
}
else
{
if (dest.DisplayName is null)
{
return src.Name;
}
else
{
return "Some other value when no conditions were met.";
}
}
}));
It would be a cool feature to have but I don't see it anywhere in Automapper documentation.
This should however work in your case if the logic is not more complex.
.ForMember(dest => dest.DisplayName, opt =>
{
opt.MapFrom(src => src.Name + (location.Parent != null ? ", " + src.Parent.Name : null));
})

NHibernate and MySql Missing column Exception

From time to time my web application begins throwing the following errors.
Using NHibernate 4.0.0.4000 and MySql.Data 6.8.3
Stack Trace
ERROR [(null)] - Message:could not execute query
NHibernate log
NHibernate.Util.ADOExceptionReporter WARN - System.IndexOutOfRangeException: Could not find specified column in results:
Once once of these errors occur it begins to happen frequently until the web application is restart.
It's odd that it only happens to some users and not all. Also I noticed in this particular log message the values of p4 and p5 should be swapped.
Is this an issue with the query cache?
Does anyone have some insight into why this is happening?
If it helps here is the gnarly query (but I see this error on much simpler queries as well)
FunderInfoViewModel funderDto = null;
Funder funderAlias = null;
Contact contactAlias = null;
var totalOpportunitiesAwardedCount = QueryOver.Of<Opportunity>()
.Where(o => o.Funder.Id == funderAlias.Id)
.And(o => o.Status == OpportunityStatus.Awarded || o.Status == OpportunityStatus.AwardedClosed)
.SelectList(list => list
.SelectCount(o => o.Id));
var totalOpportunitiesAwardedSum = QueryOver.Of<Opportunity>()
.Where(o => o.Funder.Id == funderAlias.Id)
.And(o => o.Status == OpportunityStatus.Awarded || o.Status == OpportunityStatus.AwardedClosed)
.SelectList(list => list
.SelectSum(o => o.AmountAwarded));
var totalOpportunitiesCount = QueryOver.Of<Opportunity>()
.Where(o => o.Funder.Id == funderAlias.Id)
.SelectList(list => list
.SelectCount(o => o.Id));
IEnumerable<FunderInfoViewModel> funders = _session.QueryOver(() => funderAlias)
.Left.JoinAlias(f => f.Contacts, () => contactAlias, x => x.IsDefault)
.Where(o => o.Organization.Id == organizationId)
.SelectList(list => list
.Select(x => x.Id)
.WithAlias(() => funderDto.Id)
.Select(x => x.Name)
.WithAlias(() => funderDto.Name)
.Select(x => x.Description)
.WithAlias(() => funderDto.Description)
.Select(x => x.AreasOfInterest)
.WithAlias(() => funderDto.AreasOfInterest)
.Select(x => x.Type)
.WithAlias(() => funderDto.FunderType)
.Select(x => x.TaxId)
.WithAlias(() => funderDto.TaxId)
.Select(x => x.PhoneNumber)
.WithAlias(() => funderDto.PhoneNumber)
.Select(x => x.FaxNumber)
.WithAlias(() => funderDto.FaxNumber)
.Select(x => x.EmailAddress)
.WithAlias(() => funderDto.EmailAddress)
.Select(x => x.Website)
.WithAlias(() => funderDto.Website)
.Select(x => x.CustomLink)
.WithAlias(() => funderDto.CustomLink)
.Select(x => x.MinimumFundingRange)
.WithAlias(() => funderDto.MinimumFundingRange)
.Select(x => x.MaximumFundingRange)
.WithAlias(() => funderDto.MaximumFundingRange)
.Select(() => contactAlias.FirstName)
.WithAlias(() => funderDto.PrimaryContactFirstName)
.Select(() => contactAlias.LastName)
.WithAlias(() => funderDto.PrimaryContactLastName)
.Select(() => contactAlias.Title)
.WithAlias(() => funderDto.PrimaryContactTitle)
.SelectSubQuery(totalOpportunitiesAwardedCount)
.WithAlias(() => funderDto.AwardedOpportunitiesCount)
.SelectSubQuery(totalOpportunitiesAwardedSum)
.WithAlias(() => funderDto.AwardedOpportunitiesValue)
.SelectSubQuery(totalOpportunitiesCount)
.WithAlias(() => funderDto.OpportunitiesCount)
)
.OrderBy(f => f.Name)
.Asc
.TransformUsing(Transformers.AliasToBean<FunderInfoViewModel>())
.List<FunderInfoViewModel>();
Ok got the problem. Its because some of the arguement you are passing in your prepared statement is null, thats why this error. I had similar issue earlier and i solved it by checking only if it is not null add it in query filtering.
Also there is another possibility of row lock by any other query. Are you using lock in mysql query?
2nd problem seems like see the solution stated below
https://forums.asp.net/t/1230295.aspx?IDataReader+Could+not+find+specified+column+in+results+

automapper conditional Custom Value Resolver

Is it possible to use a custom value resolver in automapper only if a certain condition is met?
In my case I only want to update the value with the custom value resolver if the destination is not null.
This is an example of my code. Basically I need to add the condition onto this. Is it possible?
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => op.ResolveUsing<ResponseXmlValueResolver>()
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name")))
I think Eris' solution would have work; It was just grammatical errors.
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => {
op.Condition(src => src != null);
op.ResolveUsing<ResponseXmlValueResolver>();
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name"));
});
Is this what you wanted?
If the destination is null, the mapping will be ignore.
If the destination is null, the mapping (with the customer resolved) will be apply.
As Conditions are evaluated after resolving member, like it's said here, none of the previous answers are correct.
You should rather use PreCondition this way:
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => {
op.PreCondition(src => src != null);
op.ResolveUsing<ResponseXmlValueResolver>();
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name"));
});
Will this work? (I don't have a windows box in front of me at the moment)
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => op.Condition(src => src != null)
.ResolveUsing<ResponseXmlValueResolver>()
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name")))

NHibernate QueryOver and string.format

I am working with QueryOver in NHibernate and I want to customize one property of my projected DTO using the following syntax:
IEnumerable<PersonResponseMessage> persons =
session.QueryOver<PersonEntity>()
.SelectList(list => list
.Select(p => p.Active).WithAlias(() => dto.Active)
.Select(p => p.Alert).WithAlias(() => dto.Alert)
.Select(p => p.Comments).WithAlias(() => dto.Comments)
.Select(p => string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id)).WithAlias(() => dto.DetailsUrl)
)
.TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
.List<PersonResponseMessage>();
Unfortunately NHibernate cannot do this and throws an exception saying that:
Variable P referenced from scope "" is not defined
There are in common two ways. Partially we can move that concat operation on the DB side, as documented here:
16.7. Projection Functions
In this case, we'll use the Projections.Concat:
.SelectList(list => list
.Select(p => p.Active).WithAlias(() => dto.Active)
.Select(p => p.Alert).WithAlias(() => dto.Alert)
.Select(p => p.Comments).WithAlias(() => dto.Comments)
// instead of this
//.Select(p => string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id))
// .WithAlias(() => dto.DetailsUrl)
// use this
.Select(p => Projections.Concat(uriHelper.Root, Projections.Concat, p.Id))
.WithAlias(() => dto.DetailsUrl)
)
.TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
.List<PersonResponseMessage>();
But I would vote for ex-post processing on the Application tier, in C#:
.SelectList(list => list
.Select(p => p.Active).WithAlias(() => dto.Active)
.Select(p => p.Alert).WithAlias(() => dto.Alert)
.Select(p => p.Comments).WithAlias(() => dto.Comments)
// just the ID
.Select(p => p.Id).WithAlias(() => dto.Id)
)
.TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
.List<PersonResponseMessage>()
// do the concat here, once the data are transformed and in memory
.Select(result =>
{
result.DetailsUrl = string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id)
return result;
});

Automapper - Ignore mapping with condition

I'm using automapper and I would like to know if it's possible to ignore a mapping of a field when that's null.
That's my code:
.ForMember(dest => dest.BusinessGroup_Id,
opt => opt.MapFrom(src => (int)src.BusinessGroup))
src.BusinessGroup type = "enum"
dest.BusinessGroup_Id = int
The objective it's to ingore that Mapping if src.BusinessGroup = null.
I think NullSubstitute option will do the trick
.ForMember(d => d.BusinessGroup_Id, o => o.MapFrom(s => (int?)s.BusinessGroup));
.ForMember(d => d.BusinessGroup_Id, o => o.NullSubstitute(0));
BTW you can write your conditions in mapping action:
.ForMember(d => d.BusinessGroup_Id,
o => o.MapFrom(s => s.BusinessGroup == null ? 0 : (int)s.BusinessGroup));
UPDATE if you cannot assign some default value to your property, you can just ignore it and map only not nulls:
.ForMember(d => d.BusinessGroup_Id, o => o.Ignore())
.AfterMap((s, d) =>
{
if (s.BusinessGroup != null)
d.BusinessGroup_Id = (int)s.BusinessGroup;
});

Categories