I have two entities: UserProfile and Inbox
This is my code for UserProfile:
public partial class UserProfile {
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
//--------------------------
public virtual ICollection<Messages.Inbox> Inboxes { get; set; }
}
public partial class UserProfile {
public static void Configure(DbModelBuilder mb) {
mb.Entity<UserProfile>().HasKey(up => up.Id);
}
}
And this is my code for Inbox:
public partial class Inbox {
public int Id { get; set; }
public int UserId { get; set; }//FK to userProfile
public Accounts.UserProfile User { get; set; }
public DateTime MessageDateTime { get; set; }
public string Context { get; set; }
public int SenderId { get; set; }//FK to userProfile
public Accounts.UserProfile Sender { get; set; }
}
public partial class Inbox {
public static void Configure(DbModelBuilder mb) {
mb.Entity<Inbox>().HasKey(up => up.Id);
mb.Entity<Accounts.UserProfile>().HasMany(up => up.Inboxes)
.WithRequired(p => p.User)
.HasForeignKey(p => p.UserId);
mb.Entity<Accounts.UserProfile>().HasMany(up => up.Inboxes)
.WithRequired(p => p.Sender)
.HasForeignKey(p => p.SenderId);
}
}
How can I manage these two relations between Inbox and UserProfile?
If you have a DTO that you expose to the user/ui to add inbox, that dto should not include User/Sender properties, just the Ids SenderId/UserId, but keep them in the Inbox entity, as you need to load some details about the User/Sender from the inbox entity, so instead of joining with UserProfile table, you can include the it using Entity-framework. hope that helps.
Related
I'd like Db use Code-First
What I did:
public class Responses
{
public int UserId { get; set; }
public virtual AppUser AppUser { get; set; }
public int QuestionId { get; set; }
public virtual Question Question { get; set; }
public string Answer { get; set; }
}
public class Question
{
public int idQuestion { get; set; }
public string TextQuestion { get; set; }
public ICollection<Responses> Responses { get; set; }
}
public class AppUser : IdentityUser<int, AppUserLogin, AppUserRole, IdentityUserClaimBase>, ICRMRepository, IEditableEntity, IEntityBase
{
public int idUser {get;set;}
public ICollection<Responses> Responses { get; set; }
}
next to I go to DbContext:
modelBuilder.Entity<Responses>()
.HasKey(x => new {x.UserId, x.QuestionId});
modelBuilder.Entity<Responses>()
.HasOne(x=>x.User)
Cannot resolve symbol "HasOne"
what should I do if I would like get db like this?
How do I configure my association with fluent API? Or is there a better way to create the association table?
UPD
Change Responses class, replace AppUser property by User
public class Responses
{
public int UserId { get; set; }
public virtual AppUser User{ get; set; }
........
}
and use this code for Db context
modelBuilder.Entity<Response>(entity =>
{
entity.HasOne(d => User)
.WithMany(p => p.Responses)
.HasForeignKey(d => d.UserId);
entity.HasOne(d => d.Question)
.WithMany(p => p.Responses)
.HasForeignKey(d => d.QuestionId;
});
I could solve it:
public class AppUser : IdentityUser<int, AppUserLogin, AppUserRole, IdentityUserClaimBase>, ICRMRepository, IEditableEntity, IEntityBase
{
public ICollection<Response> Responses { get; set; }
}
public class Question
{
public int Id { get; set; }
public string TextQuestion { get; set; }
public ICollection<Response> Responses { get; set; }
}
public class Response
{
public int Id { get; set; }
***public int AppUserId { get; set; }***
public int QuestionId { get; set; }
public virtual AppUser AppUser { get; set; }
public virtual Question Question { get; set; }
public string Answer { get; set; }
}
db context:
public DbSet<Question> Questions { get; set; }
public DbSet<Response> Responses { get; set; }
EF6 understand what I wanted
How do I define the relationships here with EF Core?
I have an Employee table which has multiple Jobs
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<HourlyRate> DefaultRate { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
public List<Address> Addresses { get; set; }
public List<Job> Jobs { get; set; }
public bool Deleted { get; set; }
}
And the Job class has an Employee object to navigate back to the employee and the Job has multiple Directors which are also Employees
public class Job
{
public int JobId { get; set; }
public Employee Employee { get; set; }
public JobType Type { get; set; }
public Department Department { get; set; }
public List<Employee> Directors { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
}
This is my DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasMany(employee => employee.Jobs)
.WithOne(i => i.Employee);
}
Initially the Job only had a single Director and everything was good but the requirement has changed to have multiple directors and ef removed the Director column from the Job table and added a JobId column to the Employee table but the problem is that if i add that director to a second job by job.Directors.Add(director) EF overrides the job id of the of the director and the director is being removed from the previous job
I am using EF Core 2.2
if a Job has only 1 Employee but multiple Directors (also Employee)
add public int EmployeeId {get; set;} to your Job class and add this
modelBuilder
.Entity<Job>()
.HasMany(p => p.Directors)
.WithMany(p => p.Jobs));
also, change List<> to ICollection<>
You should tell EF through fluent API that there's a 1-to-many relationship from Employee to Job. Otherwise, EF may get confused.
The many-to-many relationship needs a junction table and matching entity in the model which you'll also need to configure through fluent API. You'll define two 1-to-many relationships from Employee and Job to that new entity. EF core does not directly support such relationships before 5.0.
If you are targeting SQL, then you need to mark at least one of the relationships as OnDelete(CascadeBehavior.NoAction). Otherwise, your model will generate invalid table defintions which will raise errors at creation time.
Update:
The junction table would be defined something like this.
public class Employee
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
public int EmployeeId { get; set; }
public int JobId { get; set; }
public Employee Employee { get; set; }
public Job Job { get; set; }
}
// goes in DbContext
modelBuilder.Entity<EmployeeJob>.HasKey(x => new { x.EmployeeId, x.JobId });
Try to use this code. Since your employee can have one or many jobs I added the table EmployeeJob and many-to-many relations. I think you just need to add IsDirector flag to Employee or maybe better something like an EmployeeType:
public class Employee
{
public Employee()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Key]
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public bool Deleted { get; set; }
[InverseProperty(nameof(EmployeeJob.Employee))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
public Job()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Required]
public int JobId { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
[InverseProperty(nameof(EmployeeJob.Job))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
[Key]
public int Id { get; set; }
public int EmployeeId { get; set; }
[ForeignKey(nameof(EmployeeId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Employee Employee { get; set; }
public int JobId { get; set; }
[ForeignKey(nameof(JobId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Job Job { get; set; }
}
public class EmployeeDbContext : DbContext
{
public EmployeeDbContext()
{
}
public EmployeeDbContext(DbContextOptions<EmployeeDbContext> options)
: base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<EmployeeJob> EmployeeJobs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost;Database=Employee;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EmployeeJob>(entity =>
{
entity.HasOne(d => d.Employee)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.EmployeeId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Employee");
entity.HasOne(d => d.Job)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.JobId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Job");
});
}
I've been trying to get this working for too long. I've looked at every automapper question I could find, but still no luck.
How do I configure Automapper so that it will properly map my many to many properties between my business entity and my database model?
I'm using a repository pattern using a DB first data model.
I have these entity objects:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { } }
public List<Department> Departments { get; set; }
public List<Company> Companies { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { } }
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { } }
}
I need to be able to update these. I need to map them to the db object so I can update the user.
public partial class User{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public virtual ICollection<UsersCompany> UsersCompanies { get; set; }
public virtual ICollection<UsersDepartment> UsersDepartments { get; set; }
}
public partial class UsersCompany
{
public int Id { get; set; }
public int UserId { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual User User { get; set; }
}
public partial class UsersDepartment
{
public int Id { get; set; }
public int UserId { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
public virtual User User { get; set; }
}
I have a method in which I pass the entity and attempt to map it to the db model. This is what I have right now after about 100 different attempts to get automapper to politely map my join tables...with no luck.
private DBUser ToDataModel(User user)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, DBUser>()
.ForMember(dest => dest.UsersDepartments, opt => opt.MapFrom(x => x.Departments));
cfg.CreateMap<User, DBUsersDepartment>()
.ForMember(x => x.User, y => y.MapFrom(z => z));
cfg.CreateMap<Department, DBUsersDepartment>();
});
IMapper mapper = config.CreateMapper();
return mapper.Map<DBUser>(user);
}
This is what I see after it 'maps' (notice no user information has been mapped):
If you still want to know a possible solution to your problem for the future, here are the necessary changes to your code, to get it to work.
While your DTO class User uses the direct related DTOs Department and Company, your DB class USerDb uses the many-many mappings UsersDepartment and UsersCompany.
public class User
{
...
public List<Department> Departments { get; set; }
public List<Company> Companies { get; set; }
}
public class DbUser
{
...
public List<UsersDepartment> UsersDepartments { get; set; }
public List<UsersCompany> UsersCompanies { get; set; }
}
cfg.CreateMap<UserDb, User>()
.ForMember(dest => dest.Departments, opt => opt.MapFrom(user => user.UsersDepartments.Select(userDepartment => userDepartment.Department)))
.ForMember(dest => dest.Companies, opt => opt.MapFrom(user => user.UsersCompanies.Select(userCompany => userCompany.Company)));
cfg.CreateMap<User,DbUser>()
.ForMember(dest => dest.UsersDepartments, opt => opt.MapFrom( user=>user.Departments.Select( department=> new UsersDepartment{UserId=user.Id, DepartmentId=department.Id}))))
.ForMember(dest => dest.UsersCompanies, opt => opt.MapFrom( user=>user.Companies.Select( company=> new UsersCompany{UserId=user.Id, CompanyId=company.Id}))));
I would also remove the additional Id from UsersDepartment and UsersCompany and use a combined key UsersId, DepartmentId or UseId,CompanyId instead.
Hope, that helps.
You are mapping from
public class Department{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get { return DateTime.Now; } private set { }
}
to
public partial class UsersDepartment{
public int Id { get; set; }
public int UserId { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }//No matching property in Department
public virtual User User { get; set; }//No matching property in Department
}
so it is normal that when maping Department→UsersDepartment
public virtual User User { get; set; }
and
public virtual Department Department { get; set; } will be null.
Moreover, you are creating a mapper each time you call private DBUser ToDataModel(User user), which is highly inefficient. Please consider, creating your mappers at the beginning of your application, for example if it is web application in global.asax etc
For anyone interested. I ended up going a different route, where I update each collection separately, then update the base user class.
...now off to the next issue.
I have 3 classes in my model as you can see below.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
public class MartialArtUserProfile
{
public int UserProfileId { get; set; }
public UserProfile UserProfile { get; set; }
public int MartialArtId { get; set; }
public MartialArt MartialArt { get; set; }
}
And I have a configuration class for many to many relationship as below:
public class MartialArtUserProfileConfiguration : EntityTypeConfiguration<MartialArtUserProfile>
{
public MartialArtUserProfileConfiguration()
{
HasKey(a => new { a.MartialArtId, a.UserProfileId });
HasRequired(a => a.MartialArt)
.WithMany(s => s.MartialArtUserProfiles)
.HasForeignKey(a => a.MartialArtId)
.WillCascadeOnDelete(false);
HasRequired(a => a.UserProfile)
.WithMany(p => p.MartialArtUserProfiles)
.HasForeignKey(a => a.UserProfileId)
.WillCascadeOnDelete(false);
}
}
After defining my entities an relation when I try to run Update-Database in Package Manager Console, it says:
One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'MartialArtUserProfile' has no key defined. Define the key for this EntityType.
\tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'MartialArtUserProfiles' is based on type 'MartialArtUserProfile' that has no keys defined.
What am I doing wrong?
Thanks in advance,
If I understand you are simply trying to create a many to many with a transitive table. If so this is another way to approach this. Use Fluent API to map as below. You can change the UserProfileToMartialArt to whatever you want the table name to be. Instead of creating the MartialArtUserProfile model let EF create the middle ground for you. This also specifies your keys which should get you around the error.
modelBuilder.Entity<UserProfile>()
.HasMany(b => b.MartialArts)
.WithMany(a => a.UserProfiles)
.Map(m => m.MapLeftKey("MartialArtId")
.MapRightKey("UserProfileId")
.ToTable("UserProfileToMartialArt"));
In MartialArts Model put
public IList<UserProfile> UserProfiles { get; set; }
In UserProfile Model put
public IList<MartialArt> MartialArts { get; set; }
Try doing it like this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
[InverseProperty("UserProfiles")]
public IList<MartialArt> MartialArts { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
[InverseProperty("MartialArts")]
public IList<UserProfile> UserProfiles { get; set; }
}
In EntityFramework 6.1, you don't need to do any of this - just add collections of the two types to each class and everything falls into place.
public class UserProfile {
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<MartialArt> MartialArts { get; set; }
public UserProfile() {
MartialArts = new List<MartialArt>();
}
}
public class MartialArt {
public int Id { get; set; }
public string Name { get; set; }
// *snip*
public virtual ICollection<UserProfile> UserProfiles { get; set; }
public MartialArt() {
UserProfiles = new List<UserProfile>();
}
}
These are my entities:
public class Department
{
public Department()
{
Employees = new List<Employee>();
}
public int ID { get; set; }
public string Name { get; set; }
public IList<Employee> Employees { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int DepartmentID { get; set; }
public Department Department { get; set; }
}
public class User
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public int EmployeeID { get; set; }
public Employee Employee { get; set; }
}
Department has many employees. and every user has just one employee (one to one). how can i achive this relation with fluent code first?
thanx.
Since you are not using a shared primary key, you can map it as a "One-to-Many" relationship and ignore the "Many" side.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<User>()
.HasRequired(u => u.Employee)
.WithMany()
.HasForeignKey(u => u.EmployeeId);
}