I have many to many relationship tables such as "User & Notification & UserNotification" and their entities, view models also.
There is only a difference between ViewModel and Entity classes. HasRead property is inside NotificationViewModel. How Can I map this entities to view models? I could not achieve this for HasRead property.
What I did so far is,
Mapping Configuration:
CreateMap<Notification, NotificationViewModel>();
CreateMap<User, UserViewModel>().ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)));
Notification class:
public class Notification : IEntityBase
{
public Notification()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
User Class
public class User : IEntityBase
{
public User()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
UserNotification class:
public class UserNotification : IEntityBase
{
public int Id { get; set; }
public int UserId { get; set; }
public int NotificationId { get; set; }
public bool HasRead { get; set; }
public virtual Notification Notification { get; set; }
public virtual User User { get; set; }
}
UserViewModel class
public class UserViewModel : IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public IList<NotificationViewModel> Notifications { get; set; }
}
NotificationViewModel class
public class NotificationViewModel
{
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public bool HasRead { get; set; } // this is the difference
}
In order to fix up the HasRead, maybe you can utilize the AfterMap(Action<TSource, TDestination> afterFunction) function. It's not as elegant as the rest of automapper, but it might work.
CreateMap<User, UserViewModel>()
.ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)))
.AfterMap((src, dest) =>
{
foreach (var notificationVM in dest.Notifications)
{
notificationVM.HasRead = src.UserNotification.Where(x => x.NotificationId == notificationVM.Id).Select(x => x.HasRead).FirstOrDefault();
}
});
Related
In the data access level, I have defined such an entity:
public class Instagram
{
public int Id { get; set; }
public string Username { get; set; } = null!;
public string Password { get; set; } = null!;
public string? StateData { get; set; }
public string? TwoFactorLoginInfo { get; set; }
public string? ChallengeLoginInfo { get; set; }
public bool IsActive { get; set; }
public bool IsSelected { get; set; }
public long UserId { get; set; }
public User User { get; set; } = null!;
public Proxy? Proxy { get; set; }
public int? ProxyId { get; set; }
public List<Work>? Works { get; set; }
}
At the abstraction level, there is such a DTO:
public class InstagramDto
{
public int Id { get; set; }
public string Username { get; set; } = null!;
public string Password { get; set; } = null!;
public string? StateData { get; set; }
public string? TwoFactorLoginInfo { get; set; }
public string? ChallengeLoginInfo { get; set; }
public bool IsSelected { get; set; }
public bool IsActive { get; set; }
public UserDto? User { get; set; }
public ProxyDto? Proxy { get; set; }
}
Did I form the DTO correctly? It maps well to one side (from EF to DTO), but the problem is reverse mapping. I do it like this:
CreateMap<InstagramDto, Instagram>()
.ForMember(x => x.UserId,
expression => expression.MapFrom((dto, _) => dto.User?.Id))
.ForMember(x => x.User, expression => expression.Ignore())
.ForMember(x => x.Proxy,
expression => expression.MapFrom((dto, _) => dto.Proxy?.Id))
.ForMember(x => x.Proxy, expression => expression.Ignore());
CreateMap<Instagram, InstagramDto>();
That is, InstagramDto must have UserDTO and ProxyDto so that I can correctly map the entity from DTO to ef. At the same time, the user may have some other navigation properties that are not involved when receiving Instagram. This means that I cannot update the User, as its navigation properties are not loaded in this situation. Is this the right approach or would it be better to do so:
public int Id { get; set; }
public string Username { get; set; } = null!;
public string Password { get; set; } = null!;
public string? StateData { get; set; }
public string? TwoFactorLoginInfo { get; set; }
public string? ChallengeLoginInfo { get; set; }
public bool IsSelected { get; set; }
public bool IsActive { get; set; }
public long UserId { get; set; }
public int ProxyId { get; set; }
Patient.cs //This is Patient Model Class
namespace HMS.Models
{
public class Patient
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
public int age { get; set; }
public int Weight { get; set; }
public string Gender { get; set; }
public string Address { get; set; }
public string PhoneNo { get; set; }
public string Disease { get; set; }
[JsonIgnore]
public IList<DoctorPatient> DoctorPatients { get; set; }
public InPatient InPatients { get; set; }
public OutPatient OutPatients { get; set; }
}
}
InPatient.cs //This InPatient Model Class
namespace HMS.Models
{
public class InPatient
{
[ForeignKey("Patient")]
public string InPatientId { get; set; }
public string RoomNo { get; set; }
public DateTime DateOfAddmission { get; set; }
public DateTime DateOfDischarge { get; set; }
public int Advance { get; set; }
public string LabNo { get; set; }
public Patient Patient { get; set; }
}
}
Here Patient and InPatient Attribute have one-to-one relationship
ViewInPatient.cs
namespace HMS.Models
{
public class ViewInPatient
{
public string Name { get; set; }
public int age { get; set; }
public int Weight { get; set; }
public string Gender { get; set; }
public string Address { get; set; }
public string PhoneNo { get; set; }
public string Disease { get; set; }
public string RoomNo { get; set; }
public DateTime DateOfAddmission { get; set; }
public DateTime DateOfDischarge { get; set; }
public int Advance { get; set; }
public string LabNo { get; set; }
}
}
Here is my DbContext class
public class ApplicationDbContext:DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options):base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DoctorPatient>()
.HasOne(x => x.Doctor)
.WithMany(dp => dp.DoctorPatients)
.HasForeignKey(di => di.DoctorId);
modelBuilder.Entity<DoctorPatient>()
.HasOne(y => y.Patient)
.WithMany(dp => dp.DoctorPatients)
.HasForeignKey(pi => pi.PatientId);
}
public DbSet<Patient> Patients { get; set; }
public DbSet<Doctor> Doctors { get; set; }
public DbSet<DoctorPatient> DoctorPatients { get; set; }
public DbSet<InPatient> InPatients { get; set; }
//public DbQuery<ViewInPatient> ViewInPatients { get; set; }
}
How to get all data of both Patients and InPatients Table like in ViewInPatient class? (I tried to create a view in sql server but in add table window it shows InPatient instead of InPatients and it return null value)
You can join both models in a Linq expression and return ViewInPatient list:
var ViewInPatient_set =
YourContext
.InPatients
.Select(i=> new ViewInPatient()
{
Name = i.Patient.Name,
// ...
RoomNo = i.RoomNo,
// ...
}
)
.ToList(); // <-- transform to list is optional
I have a simple query
clients = _context.Clients
.Include(e => e.TrainerClients)
.Include(e => e.User)
.Where(e => e.TrainerClients.All(f => f.Status == UserStatus.Linked));
that returns:
but I get a warning message if executed as-is:
The Include operation for navigation '[e].User' is unnecessary and was ignored because the navigation is not reachable in the final query results.
However, if I comment out the Include(e => e.User) from the query, I don't get the warning, but I also don't get the User data.
So why am I getting this warning, and how can I refine my query to not get the warning?
UPDATE:
Client model:
public class Client : BaseModel
{
public string UserId { get; set; }
[ForeignKey(nameof(UserId))]
public ApplicationUser User { get; set; }
public DateTime LastPaidDate { get; set; }
public string CreditCardNumber { get; set; }
public ICollection<TrainerClient> TrainerClients { get; set; }
}
ApplicationUser:
public class ApplicationUser : IdentityUser
{
public bool Facebook { get; set; }
public string ProfilePicture { get; set; }
public string CountryCode { get; set; }
public string State { get; set; }
public string City { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual DateTime? LastLoginTime { get; set; }
public virtual DateTime? RegistrationDate { get; set; }
public string Address { get; set; }
public string ZipCode { get; set; }
public int StateId { get; set; }
//Properties Client used just for relation
public Client Client { get; set; }
public ProfileSetting ProfileSetting { get; set; }
}
TrainerClient:
public class TrainerClient : BaseModel
{
public int TrainerId { get; set; }
[ForeignKey(nameof(TrainerId))]
public Trainer Trainer { get; set; }
public int ClientId { get; set; }
[ForeignKey(nameof(ClientId))]
public Client Client { get; set; }
public Enum.UserStatus Status { get; set; }
public Enum.RequestType RequestType { get; set; }
public string RequestDate { get; set; }
}
I have following tables: User, UserGroups, and UserInGroups. You can see them on picture below. When i call User i want to be able to get Groups that user is in (UserInGroups). I am reading materials about EntityFramework but i am unable to make connections in code to to that, what am i missing? Do i need to connect them onModelCreating?
Currently i am not getting any data from UserInGroup or UserGroups.
DB looks like this
My classes look like this:
public class User : BaseEntity
{
public int RoleId { get; set; }
public Role Role { get; set; }
public UserGroup UserGroup { get; set; }
public UserInGroup UserInGroup { get; set; }
}
public class UserGroup : BaseEntity
{
public UserGroup()
{
Users = new List<User>();
UserInGroups = new List<UserInGroup>();
}
[StringLength(255)]
public string Name { get; set; }
public string KeyName { get; set; }
public List<User> Users { get; set; }
public List<UserInGroup> UserInGroups { get; set; }
}
public class UserInGroup : BaseEntity
{
public UserInGroup()
{
Users = new List<User>();
UserGroups = new List<UserGroup>();
}
public int UserId { get; set; }
public User User { get; set; }
public int UserGroupId { get; set; }
public UserGroup UserGroup { get; set; }
public List<User> Users { get; set; }
public List<UserGroup> UserGroups { get; set; }
}
DbContext:
public DbSet<GlobalSettings> GlobalSettings { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroups { get; set; }
public DbSet<UserInGroup> UsersInGroups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<GlobalSettings>().Property(x => x.Key).HasColumnAnnotation("Index", new IndexAnnotation(new[] { new IndexAttribute("Index_VariablenName") { IsClustered = false, IsUnique = true } }));
}
public abstract partial class BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
public class User : BaseEntity
{
public string Username { get; set; }
public string Title { get; set; }
public FirstName { get; set; }
public string LasName { get; set; }
public Genders Gender { get; set; }
public string Email { get; set; }
public int RoleId { get; set; }
public string TechUser { get; set; }
public DateTime TechChangeDate { get; set; }
public int TechVersion { get; set; }
public bool IsDeleted { get; set; }
public virtual Role Role { get; set; }
public virtual ICollection<UserInGroup> UserInGroups { get; set; }
}
public class UserInGroup : BaseEntity
{
public int UserId { get; set; }
public int UserGroupId { get; set; }
public string TechUser { get; set; }
public DateTime TechChangeDate { get; set; }
public int TechVersion { get; set; }
public bool IsDeleted { get; set; }
public virtual User User { get; set; }
public virtual UserGroup UserGroup { get; set; }
}
public class UserGroup : BaseEntity
{
public string Name { get; set; }
public string KeyName { get; set; }
public string TechUser { get; set; }
public DateTime TechChangeDate { get; set; }
public int TechVersion { get; set; }
public bool IsDeleted { get; set; }
}
public class Role : BaseEntity
{
public string Name { get; set; }
}
public enum Genders
{
Male = 1,
Female = 2
}
You can use two methods to fill navigation properties. First is lazy-loading and second is explicit specifying of required properties.
For lazy-loading you should declare your navigation properties as virtual:
public class User : BaseEntity
{
public int RoleId { get; set; }
public virtual Role Role { get; set; }
public virtual UserGroup UserGroup { get; set; }
public virtual UserInGroup UserInGroup { get; set; }
}
public class UserGroup : BaseEntity
{
public UserGroup()
{
Users = new HashSet<User>(); // HashSet is more effective than List
UserInGroups = new HashSet<UserInGroup>();
}
[StringLength(255)]
public string Name { get; set; }
public string KeyName { get; set; }
public virtual ICollection<User> Users { get; set; } // ICollection is less restrective
public virtual ICollection<UserInGroup> UserInGroups { get; set; }
}
Now, you can load f.e. single user:
var justUser = dbContext.Users.Single(u => u.Id == 100);
When you need its properties they will by transparently loaded:
foreach (var userInGroup in user.UsersInGroups) // here is second loading
{
. . .
}
The second way is the calling of the Include method to explicit specifying required properties:
var userWithGroups = dbContext.Users
.Include(u => u.UserInGroups) // include single navigation property
.Include(ugs => ugs.Groups.Select(ug => ug.Group)) // include collection navigation property
.Single(u => u.Id == 100); // get the user with specified id and filled specified properties
i have a few entities. Each entity includes many optional documents defined in the inherited BaseEntity.
public class Address : BaseEntity
{
public virtual Address Parent { get; set; }
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Additional { get; set; }
public string Street { get; set; }
public string HousNr { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public virtual Document Image { get; set; }
public virtual ICollection<AddressContact> AddressContacts { get; set; }
public virtual ICollection<Address> AddressPersons { get; set; }
}
public abstract class BaseEntity
{
public Guid Id { get; set; }
public bool IsDeleted { get; set; }
public bool IsSystem { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Document> Documents { get; set; }
public DateTime CreatedTime { get; set; }
public string CreatedUser { get; set; }
public DateTime? LastModifiedTime { get; set; }
public string LastModifiedUser { get; set; }
}
and my Document-Entity:
public class Document : BaseEntity
{
public string Filename { get; set; }
public string MIMEType { get; set; }
public int? Length { get; set; }
public byte[] Content { get; set; }
}
and here my base mapping:
public abstract class BaseEntityConfiguration<TEntity> : EntityTypeConfiguration<TEntity> where TEntity : BaseEntity
{
public abstract string TableName { get; }
public virtual bool HasDocument { get {return false;} }
public BaseEntityConfiguration()
{
HasKey(x => x.Id);
Property(x => x.CreatedUser).IsRequired().HasMaxLength(100);
if (HasDocument)
{
//TODO: general-Mapping for Documents??
}
else
{
Ignore(x => x.Documents);
}
ToTable(TableName);
}
}
how can i define my code-first-mapping, so that in the end only one general document table for all comming entities exists?