I am using Automapper. In that, I mapped the DTO with the database table. In that one, I need to check one condition and then take the value.
CreatedBy = mapper.Map<UserProperties>((from createdByUser in context.persons.Where(x => x.IsActive && x.Id == notes.CreatedBy) select createdByUser).FirstOrDefault())
This is my code.
User Properties Class:
public string DisplayName { get; set; }
public int Id { get; set; }
public bool IsUser { get; set; }
public int NotesCount {get;set;}
Persons
public string DisplayName { get; set; }
public int Id { get; set; }
public int RoleId{ get; set; }
public int NotesCount {get;set;}
public string Notes{get;set;}
public string Comments {get;set;}
The below code is the automapper configuration in the start up file.
Mapping Profile Class
In persons, have the field roleId. I need to assign the values for the IsUser field in the User properties class by checking the condition like RoleId field in Persons is equal to 2.
How to Check the condition using the automapper?
Automapper Version: 9.0.0
You need to add a ForMember clause to your mapping to add the condition - here's a working example (which took longer than it should have, because you posted an image of your code instead of the actual code. This is why on SO you should always post code, not images.)
void Main()
{
var mapperConfig =
new MapperConfiguration(mc => mc.AddProfile<MappingProfile>());
var mapper = mapperConfig.CreateMapper();
var notAUser = new Persons { RoleId = 1};
var isAUser = new Persons { RoleId = 2};
var shouldBeNotAUser = mapper.Map<UserProperties>(notAUser);
var shouldBeAUser = mapper.Map<UserProperties>(isAUser);
Console.WriteLine(shouldBeNotAUser.IsUser);
Console.WriteLine(shouldBeAUser.IsUser);
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Persons, UserProperties>()
.ForMember(destination => destination.IsUser,
options => options.MapFrom(src => src.RoleId == 2));
}
}
class UserProperties
{
public string DisplayName { get; set; }
public int Id { get; set; }
public bool IsUser { get; set; }
public int NotesCount { get; set; }
}
class Persons
{
public string DisplayName { get; set; }
public int Id { get; set; }
public int RoleId { get; set; }
public int NotesCount { get; set; }
public string Notes { get; set; }
public string Comments { get; set; }
}
Output:
False
True
However
Your mapping configuration code should not have to 'know' about what RoleID indicates a user. Your Person class should be where that knowledge is held, so that should have either an IsUser() method or a get-only IsUser property (with the NotMapped attribute) which returns RoleId == 2: in the former case you would still need ForMember but in the latter case you wouldn't, though if you do map back from UserProperties to Persons you would need something there to handle it - again, this should be in the Persons class and not in the mapper config. Maybe SetAsUser() that sets the RoleId.
Related
I have a method that creates a Course. The Course retains the Teacher and their id. But after adding the Course, TeacherID has the value but Teacher has null. I think the problem is in the mapping. CourseAddRequest only has a teacherID, how can I add a Teacher?
AddCourse:
public CourseResponse AddCourse(CourseAddRequest courseAddRequest, Guid teacherId)
{
var teacher = _uniDbContext.Teachers
.Include(t => t.Courses)
.SingleOrDefault(t => t.Id == teacherId);
if (teacher == null)
throw new Exception("User doesn't exist");
var course = _mapper.Map<Course>(courseAddRequest);
teacher.Courses.Add(course);
_uniDbContext.Teachers.Update(teacher);
_uniDbContext.Courses.Update(course);
_uniDbContext.Courses.Add(course);
_uniDbContext.SaveChanges();
return _mapper.Map<CourseResponse>(course);
}
Course:
public class Course : BaseEntity
{
public string Header { get; set; }
public string Description { get; set; }
public Guid TeacherId { get; set; }
public Teacher Teacher { get; set; }
public List<Student> StudentsOnCourse { get; set; } = new List<Student>();
}
CourseResponse:
public class CourseResponse
{
public Guid Id { get; set; }
public string Header { get; set; }
public string Description { get; set; }
public TeacherResponse Teacher { get; set; }
public Guid TeacherId { get; set; }
public IEnumerable<StudentResponse> Students { get; set; }
}
CourseAddRequest:
public class CourseAddRequest
{
public string Header { get; set; }
public string Description { get; set; }
public Guid TeacherId { get; set; }
}
CourseProfile:
public class CourseProfile : Profile
{
public CourseProfile()
{
CreateMap<CourseAddRequest, Course>();
CreateMap<Course, CourseResponse>();
}
}
TeacherProfile:
public class TeacherProfile : Profile
{
public TeacherProfile()
{
CreateMap<TeacherAddRequest, Teacher>();
CreateMap<Teacher, TeacherResponse>();
}
}
You guessed right. When you execute the mapping from CourseAddRequest to Course it has no Teacher Therefore the Course teach will be null.
var course = _mapper.Map<Course>(courseAddRequest);
Assuming you're using EntityFramework or another ORM it'll be able to do the insertion correctly due to the existence of the Teacher that you've referenced in the Course via TeacherId property.
And while you add the Course to the teacher's in line 11 of your method, This still leaves the Teacher property null in course. As a result, when you map it to CourseResponse you get null.
There's two way to fix this, First, you can add the teach to your course object So the mapper finds the teacher before mapping to CourseResponse in the return statement.
course.Teacher = teacher;
Or map the teacher object directly to the response.
var courseResponse = _mapper.Map<CourseResponse>(course);
courseResponse.Teacher = _mapper.Map<TeacherResponse>(teacher);
return courseResponse;
I am struggling a bit to wrap my head around Entity Framework and It's driving me crazy. I have an target object that I'd like to populate:
public class ApiInvitationModel
{
public int Id { get; set; }
public EventModel Event { get; set; }
public UserModel InvitationSentTo { get; set; }
public UserModel AttendingUser { get; set; }
}
The schemas of the above models are:
public class EventModel {
public int Id? { get; set; }
public string Name { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set }
public OrganizationModel HostingOrganization { get; set; }
public Venue Venue { get; set; }
public string Price { get; set; }
}
public class UserModel {
public int Id? { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string MobileNumber { get; set; }
public List<OrganizationModel> Organizations { get; set; }
}
public class OrganizationModel {
public int Id? { get; set; }
public stirng Name { get; set; }
public string Address { get; set; }
public UserModel PrimaryContact { get; set; }
}
The above schemas are simplified for the purpose of the question and are the models we intend to return via API.
The problem is the origin schemas in the database is very different and I'm trying to map the database objects to these objects via Entity Framework 6.
My attempted solution was to try and nest the models via a query but that didn't work and I'm not sure where to go from here besides making numerous calls to the database.
public List<ApiInvitationModel> GetInvitations(int userId) {
using (var entities = new Entities()) {
return entities.EventInvitations
.Join(entities.Users, invitation => invitiation.userId, user => user.id, (invitation, user) => new {invitation, user})
.Join(entities.Events, model => model.invitation.eventId, ev => ev.id, (model, ev) => new {model.invitation, model.user, ev})
.Join(entities.organization, model => model.user.organizationId, organization => organization.id, (model, organization) => new ApiInvitationModel
{
Id = model.invitation.id,
Event = new EventModel {
Id = model.event.id,
Name = model.event.name,
StartDate = model.event.startDate,
EndDate = model.event.endDate,
HostingOrganization = new OrganizationModel {
Id = model.invitation.hostingId,
Name = model.event.venueName,
Address = model.event.address,
PrimaryContact = new UserModel {
Name = model.event.contactName,
PhoneNumber = model.event.contactNumber,
}
}
...
},
InvitedUser = {
}
}
).ToList();
}
}
As you can see above, there's quite a bit of nesting going on but this doesn't work in Entity Framework 6 as far as I am aware. I keep getting the following errors:
"The type 'Entities.Models.API.UserModel' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.",
Based on the above error, I assumed that each of the model initiatilizations would need to be the same (i.e. initializing the values as the same ApiInvitationModel in each join in the same order) but that produces the same error.
What would be the best approach to handling this, keepign in mind the source database doesn't have foreign keys implemented?
I would like to filter my 'TranslationSet' entities, based on their 'Translations' Collection Navigation Property.
E.g.
If a 'Translation' has a 'LanguageId' of 5 (Italian), then the 'TranslationSet' that contains this 'Translation' should be removed from the result.
Here are my Entity classes:
public class Language
{
public int LanguageId { get; set; }
public string NationalLanguage { get; set; }
//Make table multi tenanted.
public int TenantId { get; set; }
public ApplicationTenant Tenant { get; set; }
public List<Translation> Translation { get; set; } = new List<Translation>();
}
public class Translation
{
public int TranslationId { get; set; }
public string TranslatedText { get; set; }
public int LanguageId { get; set; }
public Language Language { get; set; }
//Make table multi tenanted.
public int TenantId { get; set; }
public ApplicationTenant Tenant { get; set; }
public int TranslationSetId { get; set; }
public TranslationSet TranslationSet {get; set;}
}
public class TranslationSet
{
public int TranslationSetId { get; set; }
public int TenantId { get; set; }
public ApplicationTenant Tenant { get; set; }
public IEnumerable<Translation> Translations { get; set; }
}
Here is my attempt
From the image you can see that the query fails because a Translation exists with LanguageId of 5.
I have tried many many attempts to resolve this but I can't even get close the LINQ which returns my query correctly.
Please let me know if any further clarification is needed and thanks in advance to anybody who offers help.
My rule of the thumb that nearly always work is: start by querying the entities you want. That will prevent duplicates as you see in your query result. Then add predicates to filter the entities, using navigation properties. That will be:
var sets = TranslationSets // start the query here
.Where(ts => ts.Translations.All(t => t.LanguageId != 5)); // Filter
Or if you like this better:
var sets = TranslationSets // start the query here
.Where(ts => !ts.Translations.Any(t => t.LanguageId == 5)); // Filter
EF will translate both queries as WHERE NOT EXISTS.
On my previous work EF keeps query as IQueryable until I used some materialized methods, for example, ToList() or FirstOrDefault(), and I could create big, flexible and fast queries. But on my new work I noted, that sequences inside IQueryable methods have ICollection type and, of course, they have IEnumerable methods (not IQueriable). I can't understand what wrong and how I can change it. I haven't find solution in google. The version of EF is the same as on my previous work (6.1.3).
For Example, I have 2 entity classes and my own class:
public class Client // Entity
{
public int ID { get; set; }
public string FullName { get; set; }
public string Address { get; set; }
...
public virtual ICollection<Parcel> ParcelsSender { get; set; }
public virtual ICollection<Parcel> ParcelsReceiver { get; set; }
}
public class Parcel // Entity
{
public int ID { get; set; }
public string ParcelNumber { get; set; }
...
public int ClientSenderID { get; set; }
public int ClientReceiverID { get; set; }
public virtual Client ClientSender { get; set; }
public virtual Client ClientReceiver { get; set; }
}
public class ClientCustom // My class
{
public int ID { get; set; }
public string FullName { get; set; }
public bool IsInexperienced { get; set; }
}
and I created this EF query:
var clients = context.Clients
.OrderBy(x => x.FullName)
.Select(x => new ClientCustom()
{
ID = x.ID,
FullName = x.FullName,
IsInexperienced = x.ParcelsSender.Select(y => y.ID).FirstOrDefault() == 0
&& x.ParcelsReceiver.Select(y => y.ID).FirstOrDefault() == 0
})
.ToList();
In this case, the problem is that x.ParcelsSender and x.ParcelsReceiver in query are ICollection<Parcel> type; In turn, x.ParcelsSender.Select() and x.ParcelsReceiver.Select() methods returns IEnumerable<Parcel> instead IQueriable<Parcel>. As I know, that means, queries become very slowly with big amount of data.
I am facing the same issue as described in this question. Problem: my method GetAllConferences() returns correctly all the conferences from the DB, but when I return the result to the View from the controller return Ok(tripListVm) inly the first collection item is returned to the client. On the otehr side, by setting to null all the FK references (as pointed out in the SO question above) I can return correctly all the entities to the client, however this does not seem to me the proper way of proceeding.
EDIT: the solution was much simpler than I though. In the code below (I leave it in its original form for others to see it) I was not mapping the FK entities inside the ViewModel to Dto objects, but returning the model entity itself. That was the reason why I needed to null those inner references to make it work. By returning all Dtos objects, it works properly.
I have three entities involved with 1-many relationships:
public class Conference
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Venue> Venues { get; set; }
public int? LocationId { get; set; }
public Location Location { get; set; }
}
public class Venue
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public int? ConferenceId { get; set; }
public Trip Conference { get; set; }
public int? LocationId { get; set; }
public City City { get; set; }
}
public class City
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public ICollection<Conference> Conferences { get; set; }
public ICollection<Venue> Venues { get; set; }
}
In the repository, I have a method that returns the conferences and the related entities (City and Venues):
public IEnumerable<Conference> GetAllConferences()
{
return _context.Conferences
.Include(t => t.Venues)
.Include(t => t.City)
.ToList();
}
In the controller I need to use the following code to return all the results:
var conferences = _repository.GetAllConferences();
if (conferences.Any())
{
var conferenceListVm = trips.ToConferenceVmList();
//Without setting the FK references to null, I can return only the first result of the collection
foreach (var vm in conferenceListVm)
{
foreach (var pm in vm.PoinOfInterests)
{
pm.Trip = null;
}
vm.Location.Conferences = null;
vm.Location.Venues = null;
}
return Ok(conferenceListVm);
}
public static ConferenceViewModel ToConferenceVm(this Conference conference)
{
var confVm = new ConferenceViewModel();
confVm.Name = conference.Name;
confVm.City = conference.City;
confVm.Venues = conference.Venues;
return tripVm;
}
public static IEnumerable<ConferenceViewModel> ToConferenceVmList(this IEnumerable<Conference> conferences)
{
return conferences.Select(c => c.ToConferenceVm()).ToList();
}