I'm trying to add some entities using EntityFramework. I need the same model as in image
I created 3 classes:
public class UsersOrders : Entity
{
public int Order_ID { get; set; }
public int User_ID { get; set; }
public virtual User User { get; set; }
public virtual Order Order { get; set; }
}
public class User : Entity
{
public int User_ID { get; set; }
public string Surname { get; set; }
public string Name { get; set; }
public string Patronymic { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<UsersOrders> Orders { get; set; }
}
public class Order : Entity
{
public int Order_ID { get; set; }
public virtual Address Address { get; set; }
public virtual User User_ID_Courier { get; set; }
public virtual ICollection<UsersOrders> Users { get; set; }
}
Using FluentAPI trying to set primary keys in my DBContext:
builder.Entity<UsersOrders>()
.HasKey(od => new {od.Order_ID});
It works, but why in DB this field "Order_Order_ID" appears? I'm not sure but I really didn't set this field.
Thanks for any help!
UPD: OnModelCreating
protected override void OnModelCreating(DbModelBuilder builder)
{
//OrderedDishes
builder.Entity<OrderedDishes>().HasKey(od => new { od.Order_ID, od.Dish_ID, od.Number });
builder.Entity<OrderedDishes>().HasRequired(od => od.Order).WithMany(od => od.Dishes).HasForeignKey(od => od.Order_ID);
builder.Entity<OrderedDishes>().HasRequired(od => od.Dish).WithMany(od => od.Orders).HasForeignKey(od => od.Dish_ID);
//OrderStatus
builder.Entity<OrderStatus>().HasKey(os => new { os.Order_ID, os.StatusType_ID });
builder.Entity<OrderStatus>().HasRequired(os => os.Order);
builder.Entity<OrderStatus>().HasRequired(os => os.StatusType);
//DishStatus
builder.Entity<DishStatus>().HasKey(os => new { os.Order_ID, os.Dish_ID, os.Number, os.StatusType_ID });
builder.Entity<DishStatus>().HasRequired(os => os.OrderedDishes);
builder.Entity<DishStatus>().HasRequired(os => os.StatusType);
//user
builder.Entity<UsersOrders>().HasKey(od => new { od.Order_ID });
builder.Entity<UsersOrders>().HasRequired(os => os.User);
builder.Entity<UsersOrders>().HasRequired(os => os.Order);
//PriceOfDish
builder.Entity<PriceOfDish>().HasKey(t => new { t.Dish_ID, t.DateTime });
}
You have the wrong field because you rely on the Code First's convention but your are not following it correctly for the UsersOrders entity. Assuming that Id is the primary key of User class then it will look for UserId (not User_Id) which is not in your UsersOrders entity. The same goes for Order navigational property in UsersOrders.
To fix this you have to follow the convetion by refactoring your UsersOrders and use data annotations like this: (If you use this solution you must remoeve configuration for UsersOrders entity in your OnModelCreating implementation.
public class UsersOrders
{
[Key]
public int OrderID { get; set; }
[Key]
public int UserID { get; set; }
public virtual User User { get; set; }
public virtual Order Order { get; set; }
}
Or in your OnModelCreating implementation make the following changes for UsersOrders entity like this:
modelBuilder.Entity<UsersOrders>().HasKey(od => new { od.Order_ID, od.User_ID });
modelBuilder.Entity<UsersOrders>().HasRequired(os => os.User).WithMany(p => p.Orders).HasForeignKey(p => p.User_ID);
modelBuilder.Entity<UsersOrders>().HasRequired(os => os.Order).WithMany(p => p.Users).HasForeignKey(p => p.Order_ID);
In the two solutions, note that UsersOrders use composite keys using Order_Id and User_Id not only Order_ID.
Related
I have the following entities
public class Course
{
public long Id { get; set; }
public virtual ICollection<User> Users{ get; set; }
public virtual ICollection<UserCourse> CourseUsers { get; set; }
}
public class User
{
public long Id { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class UserCourse
{
public long UserId { get; set; }
public User User { get; set; }
public long CourseId { get; set; }
public Course Course { get; set; }
public bool IsRequired { get; set; }
}
with the following mappings for
UserCourse mapping :
builder
.HasOne(nav => nav.User)
.WithMany(self => self.UserCourses)
.HasForeignKey(fk => fk.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder
.HasOne(nav => nav.Course)
.WithMany(self => self.CourseUsers)
.HasForeignKey(fk => fk.CourseId)
.OnDelete(DeleteBehavior.Cascade);
and the User mapping
builder
.HasMany(nav => nav.Courses)
.WithMany(nav => nav.Users);
When trying to create a new migration I'm not exactly sure why I'm getting this.
Cannot use table 'UserCourse' for entity type 'UserCourse' since it is
being used for entity type 'UserCourse(Dictionary<string, object>)'
and potentially other entity types, but there is no linking
relationship. Add a foreign key to 'UserCourse' on the primary key
properties and pointing to the primary key on another entity typed
mapped to 'UserCourse'.
I understand what the error is, but not sure how to force the UserCourse mapping to use the User mapping generated join table or vice-versa
Also, I need the direcat mapping for OData, and the indirect mapping using the join entity to conduct operations on DbSet<UserCourse>
The public virtual ICollection<User> Users{ get; set; } in Course entity and the the public virtual ICollection<Course> Courses { get; set; } in Users entity are redundant. The entities should look more like this
public class Course
{
public long Id { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class User
{
public long Id { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class UserCourse
{
public long UserId { get; set; }
public User User { get; set; }
public long CourseId { get; set; }
public Course Course { get; set; }
}
And the OnModelCreating method should have this code
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserCourse>()
.HasKey(uc => new { uc.UserId, uc.CourseId });
modelBuilder.Entity<UserCourse>()
.HasOne(uc => uc.Course)
.WithMany(c => c.Users)
.HasForeignKey(uc => uc.CourseId);
modelBuilder.Entity<UserCourse>()
.HasOne(uc => uc.User)
.WithMany(c => c.Courses)
.HasForeignKey(uc => uc.UserId);
}
If you use EF core 5 you can directly skip the join table. It will be generated and handled by EF behind the scenes. More on the topic here https://www.thereformedprogrammer.net/updating-many-to-many-relationships-in-ef-core-5-and-above/
In the application I am working on, we have a users table, TblUser. Users within this table may belong to a single, parent user. A parent user may have multiple child users.
This relationship is maintained within a table called TblUserMapping with two columns, ParentUserId and ChildUserId corresponding with the parent's and child's TblUser.Id value. TblUser.Id is an auto-incrementing value.
How can I define this within EF Core, and would it be possible to Insert a ChildUser into TblUser and use the auto-generated Id value to also create a TblUserMapping record?
Right now I have:
[Table("TblUser")]
public class TblUser
{
public TblUser()
{
ChildUsers = new List<TblUserMapping>();
}
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<TblUserMapping> ChildUsers { get; set; }
public virtual TblUserMapping ParentUser { get; set; }
}
[Table("TblUserMapping")]
public class TblUserMapping
{
public TblUserMapping()
{
}
public int ChildUserId { get; set; }
public int ParentUserId { get; set; }
public virtual TblUser ChildUser { get; set; }
public virtual TblUser ParentUser { get; set; }
}
public class TblUserMapping : IEntityTypeConfiguration<TblUser>
{
public void Configure(EntityTypeBuilder<TblUser> entity)
{
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.UserName)
.IsRequired()
.IsUnicode(false);
}
}
public class TblUserMappingMapping : IEntityTypeConfiguration<Entities.TblUserMapping>
{
public void Configure(EntityTypeBuilder<Entities.TblUserMapping> entity)
{
entity.HasKey(e => e.ChildUserId);
entity.Property(e => e.ChildUserId)
.IsRequired();
entity.Property(e => e.ParentUserId)
.IsRequired();
entity.HasOne(e => e.ParentUser)
.WithMany(e => e.ChildUsers)
.HasForeignKey(e => e.ParentUserId);
entity.HasOne(e => e.ChildUser)
.WithOne(e => e.ParentUser)
.HasForeignKey<TblUser>(e => e.Id);
}
}
But this isn't working as I had hoped when I do:
var userInformation = await _context
.Users
.Include(entity => entity.ChildUsers)
.ThenInclude(entity => entity.ChildUser)
.Where(s => s.UserName == userName)
.FirstOrDefaultAsync();
var ChildUser = new TblUser
{
UserName = userModel.UserName,
ParentUser = new TblUserMapping()
{
ParentUser = userInfo
}
};
_context.Users.Add(ChildUser);
await _context.SaveChangesAsync();
You can attach navigation properties, and Entity Framework will populate the ids automatically when it creates them. The example you have given should work, you might need to show us how you are getting userInfo before we can see what's going on.
That being said, instead of keeping a separate mapping table, I would have each child user refer directly to their parent:
[Table("TblUser")]
public class TblUser
{
public int Id { get; set; }
public string UserName { get; set; }
public int? ParentId { get; set; }
public TblUser Parent { get; set; }
// Lazy-loading is not enabled by default in EF Core, so you don't need the 'virtual' keyword
// Also, if the initialization of a member does not depend on constructor arguments, I
// prefer this syntax instead of doing it in the constructor
public ICollection<TblUser> Children { get; set; } = new List<TblUser>();
}
You can use InverseProperty attribute in your model:
[Table("TblUser")]
public class TblUser
{
public TblUser()
{
ChildUsers = new List<TblUserMapping>();
}
public int Id { get; set; }
public string UserName { get; set; }
[InverseProperty("ChildUser")]
public virtual ICollection<TblUserMapping> ChildUsers { get; set; }
[InverseProperty("ParentUser")]
public virtual TblUserMapping ParentUser { get; set; }
}
And in other model:
[Table("TblUserMapping")]
public class TblUserMapping
{
public TblUserMapping()
{
}
public int ChildUserId { get; set; }
public int ParentUserId { get; set; }
[ForeignKey("ChildUserId")]
public virtual TblUser ChildUser { get; set; }
[ForeignKey("ParentUserId")]
public virtual TblUser ParentUser { get; set; }
}
As you see I defined these relations with attributes that means no need to came in your configuration.
Community,
I tried my first Entity Framework Core project. Now I am unable to map the properties in my models, these are NOT One-to-One relations, I would call it "One-to-Zero":
public class ContactDetails
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("CreatedById")]
public User CreatedBy { get; set; }
[ForeignKey("UpdatedById")]
public User UpdatedBy { get; set; }
[ForeignKey("DeletedById")]
public User DeletedBy { get; set; }
// foreign keys
public int? CreatedById { get; set; }
public int? UpdatedById { get; set; }
public int? DeletedById { get; set; }
}
public class User
{
public int Id { get; set; }
public string MailAdress { get; set; }
public string Password { get; set; }
[ForeignKey("ContactDetailsByUserId")]
public ContactDetails ContactDetailsByUser { get; set; }
[ForeignKey("ContactDetailsByAdminId")]
public ContactDetails ContactDetailsByAdmin { get; set; }
// foreign keys
public int? ContactDetailsByUserId { get; set; }
public int? ContactDetailsByAdminId { get; set; }
}
So ContactDetails can have three different Users.
Usercan have two different ContactDetails.
If I try to create a migration for this, I get this error:
Unable to determine the relationship represented by navigation property 'ContactDetails.CreatedBy' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I really want to do this with Attributes only, if this is possible, anyway with the Fluent-API this works:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ContactDetails>().HasOne(c => c.CreatedBy).WithOne().HasForeignKey<ContactDetails>(c => c.CreatedById).OnDelete(DeleteBehavior.ClientSetNull);
builder.Entity<ContactDetails>().HasIndex(c => c.CreatedById).IsUnique(false);
builder.Entity<ContactDetails>().HasOne(c => c.UpdatedBy).WithOne().HasForeignKey<ContactDetails>(c => c.UpdatedById).OnDelete(DeleteBehavior.ClientSetNull);
builder.Entity<ContactDetails>().HasIndex(c => c.UpdatedById).IsUnique(false);
builder.Entity<ContactDetails>().HasOne(c => c.DeletedBy).WithOne().HasForeignKey<ContactDetails>(c => c.DeletedById).OnDelete(DeleteBehavior.ClientSetNull);
builder.Entity<ContactDetails>().HasIndex(c => c.DeletedById).IsUnique(false);
builder.Entity<User>().HasOne(u => u.ContactDetailsByUser).WithOne().HasForeignKey<User>(u => u.ContactDetailsByUserId).OnDelete(DeleteBehavior.ClientSetNull);
builder.Entity<User>().HasIndex(u => u.ContactDetailsByUserId).IsUnique(false);
builder.Entity<User>().HasOne(u => u.ContactDetailsByAdmin).WithOne().HasForeignKey<User>(u => u.ContactDetailsByAdminId).OnDelete(DeleteBehavior.ClientSetNull);
builder.Entity<User>().HasIndex(u => u.ContactDetailsByAdminId).IsUnique(false);
}
Maybe the Fluent thing will help someone, but is it possible to do this with Attributes only?
Thanks for your time.
Best Regards
Stewie
I have an entity for Users and an entity for Projects.
I need to be able to assign multiple users to 3 different list properties on my project entity. I have been able to do this successfully for one property (the many to many relationship) by a join entity. I could specify the UserType on the Users table and just use the one property, but I may run into scenarios where Users may perform more than one role (type) and then that wouldn't work.
I thought I could just put the UserType on the join table (entity) but I'm at a loss as to how to build that entity in my DBContext.
Here is what I have that's working with one property defined:
ProjectEntity:
public class Project : IInt32Identity
{
public int Id { get; set; }
public string ProjectName { get; set; }
...
public bool ProjectActive { get; set; }
public List<ProjectFile> ProjectFiles { get; set; }
public List<ProjectUsers> ProjectUsers { get; set; }
public DateTime ProjectCreatedDate { get; set; }
public DateTime ProjectModifiedDate { get; set; }
}
UserEntity:
public class User : IInt32Identity
{
public int Id { get; set; }
public string UserEmail { get; set; }
...
public List<ProjectUsers> ProjectUsers { get; set; }
public DateTime UserCreatedDate { get; set; }
public DateTime UserLastLoggedInDate { get; set; }
public DateTime UserModifiedDate { get; set; }
}
JoinEntity:
public class ProjectUsers
{
public int UserId { get; set; }
public User User { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
And my OnModelCreating() in my DBContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProjectUsers>()
.HasKey(bc => new { bc.UserId, bc.ProjectId });
modelBuilder.Entity<ProjectUsers>()
.HasOne(bc => bc.User)
.WithMany(b => b.ProjectUsers)
.HasForeignKey(bc => bc.UserId);
modelBuilder.Entity<ProjectUsers>()
.HasOne(bc => bc.Project)
.WithMany(c => c.ProjectUsers)
.HasForeignKey(bc => bc.ProjectId);
}
That all works fine as I said above, but here's what I would like:
ProjectEntity:
public class Project : IInt32Identity
{
public int Id { get; set; }
public string ProjectName { get; set; }
...
public bool ProjectActive { get; set; }
public List<ProjectFile> ProjectFiles { get; set; }
public List<ProjectUsers> ProjectClients { get; set; }
public List<ProjectUsers> ProjectBuilders { get; set; }
public List<ProjectUsers> ProjectDesigners { get; set; }
public DateTime ProjectCreatedDate { get; set; }
public DateTime ProjectModifiedDate { get; set; }
}
UserEntity is the same.
JoinEntity:
public class ProjectUsers
{
public int UserId { get; set; }
public User User { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
public string UserType { get; set; }
}
Where I'm lost is on the OnModelBinding() code and I'm also not sure if EF would be smart enough to populate the lists correctly based on that UserType meta property.
Any help or guidance would be greatly appreciated.
TIA
It might seem possible to treat ProjectUser as the base class/entity, and create different class/entity/type for ProjectClient, ProjectBuilder and ProjectDesigner that are inherited from ProjectUser. And then you create tables for each type and one-to-many relationship to the project. This is typically called Table Per Type (TPT) approach.
However, TPT is not yet implemented in EF Core.
You can still achieve it using Table Per Hierarchy (TPH), but you will have just one list in the project for all project users, where UserId, ProjectId and UserType become the complex key. Project clients, builders and designers will be calculated properties off that one project user list.
Entities
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProjectUser> ProjectUsers { get; set; }
public IEnumerable<ProjectUser> ProjectClients => this.ProjectUsers
.Where(x => x.UserType == "Client");
public IEnumerable<ProjectUser> ProjectBuilders => this.ProjectUsers
.Where(x => x.UserType == "Builder");
public IEnumerable<ProjectUser> ProjectDesigners => this.ProjectUsers
.Where(x => x.UserType == "Designer");
}
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public virtual ICollection<ProjectUser> UserProjects { get; set; }
}
public class ProjectUser
{
public int UserId { get; set; }
public virtual User User { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public string UserType { get; set; }
}
Configurations
public class ProjectConfiguration : IEntityTypeConfiguration<Project>
{
public void Configure(EntityTypeBuilder<Project> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).IsRequired();
builder.HasIndex(x => x.Name).IsUnique();
builder.Ignore(x => x.ProjectBuilders);
builder.Ignore(x => x.ProjectClients);
builder.Ignore(x => x.ProjectDesigners);
builder.ToTable("Project");
}
}
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Email).IsRequired();
builder.HasIndex(x => x.Email).IsUnique();
builder.ToTable("User");
}
}
public class ProjectUserConfiguration : IEntityTypeConfiguration<ProjectUser>
{
public void Configure(EntityTypeBuilder<ProjectUser> builder)
{
builder.HasKey(x => new { x.ProjectId, x.UserId, x.UserType });
builder.Property(x => x.UserType).IsRequired();
builder.HasOne(x => x.Project)
.WithMany(x => x.ProjectUsers)
.HasForeignKey(x => x.ProjectId);
builder.HasOne(x => x.User)
.WithMany(x => x.UserProjects)
.HasForeignKey(x => x.UserId);
}
}
The virtual keyword is there for lazy loading support. If you're not doing lazy loading, you don't have to have virtual there. Also you have to [NotMapped] those 3 calculated properties, which is the same as using .Ignore in fluent API's speaking.
DbContext
public class AppDbContext : DbContext
{
public DbSet<Project> Projects { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new ProjectUserConfiguration());
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer("Data Source=.\\SQLEXPRESS;Initial Catalog=DL.SO.ProjectUsersDemo;Integrated Security=True;MultipleActiveResultSets=False;");
}
}
Nothing special here. After you add the migration and update the database, yours should look like
After seeding the database with sample data, although it's hard to show here, you can see those 3 lists are filled with correct data:
I have two tables one with a list of clients and the other whether they are active or not. I want to link them Entity Framework, however, I am struggling. The two tables were already setup and have to primary keys or foreign keys.
namespace DataWarehouse.Models
{
public class DatabaseList
{
[Key]
public string STARDB { get; set; }
public int DBClientID { get; set; }
public string ClientName { get; set; }
public DatabaseStatus DatabaseStatus { get; set; }
public ICollection<PayComponents> PayComponents { get; set; }
= new List<PayComponents>();
}
public class DatabaseStatus
{
[Key]
public string STARDB { get; set; }
public string STATUS { get; set; }
public DatabaseList DatabaseList { get; set; }
}
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{
}
public DbSet<DatabaseList> DatabaseList { get; set; }
public DbSet<DatabaseStatus> Status { get; set; }
public DbSet<PayComponents> PayComponents { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DatabaseList>()
.HasOne(p => p.DatabaseStatus)
.WithOne(i => i.DatabaseList)
.HasForeignKey<DatabaseStatus>(k => k.STARDB);
}
}
}
I was hoping that Entity Framework would see the columns STARDB and notice that it is the same in both tables and match them that way. All I want to is to add the Status column from DatabaseStatus into the Databaselist table.
Thanks.
Managed to figure it out. My database was setup properly. However, I forgot the include statement in my Repository.cs class.
public IEnumerable<DatabaseList> GetAllClients()
{
_logger.LogInformation("Get all clients was called");
var clients = _ctx.DatabaseList
.Include(d => d.DatabaseStatus)
.OrderBy(p => p.ClientName)
.ToList();
return clients;
}
Still new to C# so a bit of learning curve!