I'm trying to make a framework to abstract my Entity Layers, However for this to work I need automapper projects to work so I can Query my DTO's instead of Querying the Entities
[TestMethod]
public async Task Verify_Mapping_Projection_Behavior()
{
var projectionModifier = "Alabastar";
var services = this.GetRegisteredRestEzServiceCollection();
var serviceProvider = services.BuildServiceProvider();
var context = (AstootContext)serviceProvider.GetService<DbContext>();
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDTO>().ForMember(x => x.FirstName,
o => o.MapFrom((entity, dto) => entity.FirstName + projectionModifier));
});
var mapper = config.CreateMapper();
// Hack: we'll verify exeuction happens in sql
// using the behavioral differene between in memory and sql (case insensitivity)
var sqlOnlyModifier = projectionModifier.ToUpper();
var userDTO = mapper.ProjectTo<UserDTO>(context.Users)
.Where(x => x.FirstName.Contains(sqlOnlyModifier))
.FirstOrDefault();
Assert.IsNotNull(userDTO);
}
My test failed, so I decided to materialize the projection directly. When I materialize I can see that my projectionModifier is not being added to the firstName property.
How can I get the project to map my modifier, so that I can use my DTO's as Sql Queryables?
The 3 parameter overload for MapFrom requires assignment. This can be resolved by using the 2 parameter overload.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDTO>()
.ForMember(dto => dto.FirstName,
opt => opt.MapFrom(entity => entity.FirstName + projectionModifier));
});
Related
I recently met with an automapper. I have a question, how i can map multiple objects from my repostiory in one DTO.
public QuestionProfile()
{
CreateMap<Question, GrammarQuestionDTO>()
.ForMember(g => g.Question, q => q.MapFrom(source => source.Text)).ReverseMap();
CreateMap<List<GrammarQuestionDTO>, TestDTO>()
.ForMember(t => t.GrammarQuestion, g => g.MapFrom(source => source)).ReverseMap();
CreateMap<Question, AuditionQuestionDTO>()
.ForMember(a => a.Question, q => q.MapFrom(source => source.Text))
.ForMember(a => a.Url, q => q.MapFrom(source => source.AudioFile.Url)).ReverseMap();
CreateMap<List<AuditionQuestionDTO>, TestDTO>()
.ForMember(t => t.AuditionQuestion, a => a.MapFrom(source => source)).ReverseMap();
}
how can I map all these 4 objects?
public async Task<TestDTO> GenerateTest(LevelType level)
{
var grammarQuestions = await _questionRepository.GetGrammarQuestionAsync(level);
var auditionQuestions = await _questionRepository.GetAuditionQuestionAsync(level);
var essayTopic = await _questionRepository.GetEssayTopicAsync(level);
var speakingTopic = await _questionRepository.GetSpeakingTopicAsync(level);
var test = _mapper.Map<TestDTO>(grammarQuestions);
test = _mapper.Map(test, auditionQuestions); //there will be a conversion error
return _mapper.Map<TestDTO>(grammarQuestions);
}
You need to map it without list, just map it one to one and AutoMapper will handle the collections:
CreateMap<AuditionQuestionDTO, TestDTO>()
.ForMember(t => t.AuditionQuestion,
a => a.MapFrom(source => source.<PropertyName>)).ReverseMap();
And then on the map you should:
test = _mapper.Map<List<AuditionQuestionDTO>>(test, auditionQuestions);
I am using Automapper in C#.
Currently I am trying to map using below code.
CreateMap<Student, StudentDto>()
.ForMember(dest => dest.FeeType, opt =>
{
opt.PreCondition(src => (src.Key == "Paid"));
opt.MapFrom(src => src.Value);
});
I want to map only if the key == Paid else it should not map. However in this case , it is just passing null.
So lets say if the collection has 100 records and only 1 matches the condition other 99 records are passed as NULL.
Edit -- I am using Azure Function Apps and my Automapper version is 10.0.0
Please advise.
I think you have to update your ConfigureServices like=>
public void ConfigureServices(IServiceCollection services) {
// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc => {
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
//This line should be updated
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddJsonOptions(options => options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore);
}
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore); ofNewtonsoft.Json.NullValueHandling.Ignore, It will handle what you ask for.
Note: Please check the link of a documentation. I believe it will do the trick.
LINK
In my database I have two tables Organizations and OrganizationMembers, with a 1:N relationship.
I want to express a query that returns each organization with the first and last name of the first organization owner.
My current select expression works, but it's neither efficient nor does it look right to me, since every subquery gets defined multiple times.
await dbContext.Organizations
.AsNoTracking()
.Select(x =>
{
return new OrganizationListItem
{
Id = x.Id,
Name = x.Name,
OwnerFirstName = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).FirstName,
OwnerLastName = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).LastName,
OwnerEmailAddress = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).EmailAddress
};
})
.ToArrayAsync();
Is it somehow possible to summarize or reuse the subqueries, so I don't need to define them multiple times?
Note that I've already tried storing the subquery result in a variable. This doesn't work, because it requires converting the expression into a statement body, which results in a compiler error.
The subquery can be reused by introducing intermediate projection (Select), which is the equivalent of let operator in the query syntax.
For instance:
dbContext.Organizations.AsNoTracking()
// intermediate projection
.Select(x => new
{
Organization = x,
Owner = x.Members
.Where(member => member.Role == RoleType.Owner)
.OrderBy(member => member.CreatedAt)
.FirstOrDefault()
})
// final projection
.Select(x => new OrganizationListItem
{
Id = x.Organization.Id,
Name = x.Organization.Name,
OwnerFirstName = Owner.FirstName,
OwnerLastName = Owner.LastName,
OwnerEmailAddress = Owner.EmailAddress
})
Note that in pre EF Core 3.0 you have to use FirstOrDefault instead of First if you want to avoid client evaluation.
Also this does not make the generated SQL query better/faster - it still contains separate inline subquery for each property included in the final select. Hence will improve readability, but not the efficiency.
That's why it's usually better to project nested object into unflattened DTO property, i.e. instead of OwnerFirstName, OwnerLastName, OwnerEmailAddress have a class with properties FirstName, LastName, EmailAddress and property let say Owner of that type in OrganizationListItem (similar to entity with reference navigation property). This way you will be able to use something like
dbContext.Organizations.AsNoTracking()
.Select(x => new
{
Id = x.Organization.Id,
Name = x.Organization.Name,
Owner = x.Members
.Where(member => member.Role == RoleType.Owner)
.OrderBy(member => member.CreatedAt)
.Select(member => new OwnerInfo // the new class
{
FirstName = member.FirstName,
LastName = member.LastName,
EmailAddress = member.EmailAddress
})
.FirstOrDefault()
})
Unfortunately in pre 3.0 versions EF Core will generate N + 1 SQL queries for this LINQ query, but in 3.0+ it will generate a single and quite efficient SQL query.
How about this:
await dbContext.Organizations
.AsNoTracking()
.Select(x =>
{
var firstMember = x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner);
return new OrganizationListItem
{
Id = x.Id,
Name = x.Name,
OwnerFirstName = firstMember.FirstName,
OwnerLastName = firstMember.LastName,
OwnerEmailAddress = firstMember.EmailAddress
};
})
.ToArrayAsync();
How about doing this like
await dbContext.Organizations
.AsNoTracking()
.Select(x => new OrganizationListItem
{
Id = x.Id,
Name = x.Name,
OwnerFirstName = x.Members.FirstOrDefault(member => member.Role == RoleType.Owner).FirstName,
OwnerLastName = x.Members.FirstOrDefault(member => member.Role == RoleType.Owner)).LastName,
OwnerEmailAddress = x.Members.FirstOrDefault(member => member.Role == RoleType.Owner)).EmailAddress
})
.ToArrayAsync();
I am using Elasticsearch .Net Client 6.x, and I have some code like:
if(this.elasticClient.IndexExists("indexName").Exists){
// do something
}
this.elasticClient.CreateIndex(
newIndex,
x => x.Settings(s => s.NumberOfShards(1)).Mappings(ms => ms.Map<T>(m => m.AutoMap())));
var searchResponse = client.Search<dynamic>(
s => s.AllTypes()
.Index(
new[] { "indexName1", "indexName"}).IgnoreUnavailable().Size(size).From(from).Query(
q => q.Bool(b => b.Must(m => m.SimpleQueryString(c => c.Query(query).Lenient().AnalyzeWildcard())))));
Now in my unit test, I'd like to test the exact descriptor passed to each method, how can I do this?
var existsResponse = new Mock<ExistsResponse>();
existsResponse.Setup(x => x.Exists).Returns(false); // Oops: can't do this since Exists is not virtual, abstract
this.elasticClient.Setup(x => x.IndexExists("indexName", null)).Returns(existsResponse.Object);
this.elasticClient.Verify(
x => x.CreateIndex("IndexName", It.Is<Func<CreateIndexDescriptor, ICreateIndexRequest>>(x => /*what goes here??*/)),
Times.Once);
this.elasticClient.Verify(
x => x.Search<dynamic>(It.Is<Func<SearchDescriptor<dynamic>>>(x => /*what goes here??*/)),
Times.Once);
Should I just simply wrap the elasticClient in a class and implement every method I used, and pass it as dependency injection?
I have two collections on a source object of different types. I want to map both collections (a union of the two) to a single destination collection of a type that has all the members from both the source types. If I do this:
CreateMap<Company, CompanyResponse>()
.ForMember(x => x.Owners, m => m.MapFrom(x => x.BusinessOwners))
.ForMember(x => x.Owners, m => m.MapFrom(x => x.IndividualOwners));
It only maps the last mapping. I tried a more elaborate mapping but this appeared to break the entity framework projection integrations that automapper does. I am using ProjectTo.
This is what I tried which also does a good job of conveying the result I want.
CreateMap<Company, CompanyResponse>()
.ForMember(x => x.Owners, m => m.ResolveUsing(x => x.BusinessOwners.Select(o => new OwnerResponse
{
Type = UpdateRegistrationCommand.CompanyUpdate.OwnerType.Business,
Address = o.Address,
PercentageShareholding = o.Percentage,
BusinessName = o.Name,
BusinessNumber = o.Number
})
.Union(x.IndividualOwners.Select(o => new OwnerResponse
{
Type = UpdateRegistrationCommand.CompanyUpdate.OwnerType.Individual,
Address = o.Address,
PercentageShareholding = o.Percentage,
Title = o.Title,
FirstName = o.FirstName,
MiddleNames = o.MiddleNames,
LastName = o.LastName
}))));
Has anyone done something like this?
According to AutoMapper's documentation, you might want to try ProjectUsing<>() instead of ResolveUsing<>().