I'm creating an API with ASP .NET CORE 6.0 and I also create a Many to Many with the same table (Users), the idea is that sponsors can sponsor one or more students and students can have one or more sponsors.
These are the tables and setup I already made.
Sponsors Table
public class Sponsors
{
[Key]
public int SponsorhipId { get; set; }
public string SponsorId { get; set; } = default!;
[ForeignKey("SponsorId")]
public Users Sponsor { get; set; } = default!;
public string SponsoredId { get; set; } = default!;
[ForeignKey("SponsoredId")]
public Users Sponsored { get; set; } = default!;
}
Users Table
public class Users : IdentityUser
{
[Required]
public string IdentificationCard { get; set; } = default!;
public string FirstName { get; set; } = default!;
public string SecondName { get; set; } = default!;
public string LastName { get; set; } = default!;
public string SecondLastName { get; set; } = default!;
public DateTime DateBirth { get; set; } = default!;
public int Age { get; set; } = default!;
public Gender Gender { get; set; } = default!;
public string Photo { get; set; } = default!;
public States State { get; set; } = default!;
public ContactMethods ContactMethods { get; set; } = default!;
public string Address { get; set; } = default!;
public Grades Grades { get; set; }
public Sponsors? Sponsor1 { get; set; }
public Sponsors? Sponsor2 { get; set; }
public Comments? Comment1 { get; set; }
public Comments? Comment2 { get; set; }
public ICollection<Courses> Courses { get; set; } = default!;
public string? RefreshToken { get; set; }
public DateTime RefreshTokenExpiryTime { get; set; }
}
ModelCreating of ApplicationDbContext
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Users>().HasIndex(u => u.IdentificationCard).IsUnique();
builder.Entity<Sponsors>()
.HasOne(m => m.Sponsor)
.WithOne(t => t.Sponsor1)
.HasForeignKey<Sponsors>(m => m.SponsorId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<Sponsors>()
.HasOne(m => m.Sponsored)
.WithOne(t => t.Sponsor2)
.HasForeignKey<Sponsors>(m => m.SponsoredId)
.OnDelete(DeleteBehavior.Restrict);
}
The problem is that right now a Sponsor can only have a relationship with a Student and viceversa.
Any idea how can I modify this to allow a Sponsor have one or more relationships with a Student and viceversa?
Thanks
Starting with EF Core 5.0, many-to-many relationships are supported without explicit mapping of the join table. EntityFramework will automatically create a join table for you if you have a collection navigation property in both entities.
In your use case it would look like this (I left out some properties for the sake of simplicity).
public class Sponsor
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Student> Students { get; set; }
}
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Sponsor> Sponsors { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Sponsor> Sponsors { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost;Initial Catalog=test;Integrated Security=True;TrustServerCertificate=True");
}
}
Related
I have in database two tables: product, supplier
I want the entity framework to define the supplier of each product
I am getting data successfully for two tables but the supplier in the product table is null.
Also, the products collection in the supplier table is null.
this is my product class:
public class Product
{
public int id { get; set; }
[Column("Productname", TypeName = "ntext")]
public string Name { get; set; }
public decimal UnitPrice { get; set; }
public bool isDiscounted { get; set; }
public int quantity { get; set; }
public int SupplierId { get; set; }
[ForeignKey("SupplierId")]
public Supplier supplier { get; set; }
}
this is the class of supplier:
public class Supplier
{
public int Id { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string city { get; set; }
public string country { get; set; }
public string phone { get; set; }
public string Fax { get; set; }
public List<Product> Products { get; set; }
}
context class:
public class DbConext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
public DbConext(DbContextOptions<DbConext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().ToTable("Product");
modelBuilder.Entity<Supplier>().ToTable("Supplier");
modelBuilder.Entity<Product>().HasKey("id");
modelBuilder.Entity<Supplier>().HasKey("Id");
modelBuilder.Entity<Product>().HasOne(p => p.supplier).WithMany(s => s.Products).HasForeignKey(p => p.SupplierId);
}
}
This article might help.
You should use .Include() to load any related properties.
minimum you need is to initialize the list
public class Supplier
{
public int Id { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string city { get; set; }
public string country { get; set; }
public string phone { get; set; }
public string Fax { get; set; }
public List<Product> Products { get; set; }
public Supplier() {
this.Products = new List<Product>();
}
}
I'm trying to delete the entities related to each other when I remove a row, but it isn't deleting the related entities. It is only deleting one entity and not the others.
My model
public class Company
{
public int CompanyId { get; set;}
public string CompanyName { get; set; }
public int CompanySize { get; set; }
public string Branche { get; set;}
public string Description {get; set;}
public Recruiter Recruiter { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Recruiter
{
public int RecruiterId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Location { get; set; }
public int Compensation { get; set; }
public string Education { get; set; }
public string StartDate { get; set; }
public string Type { get; set; }
public string Profession { get; set; }
public string Language { get; set; }
public string Description { get; set; }
public string Hours { get; set; }
public bool Checked { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(post => post.Company)
.WithMany(company => company.Posts)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.SeedDatabase();
}
The call I make. So when I delete a post, I want that all the related entities are being deleted.
public Post DeclinePostRequest(int postId)
{
var request = _dbContext.Posts.Where(post => post.PostId == postId).Include(post => post.Company).ThenInclude(company => company.Recruiter).FirstOrDefault();
if(!request.Checked)
{
_dbContext.Posts.Remove(request);
_dbContext.SaveChanges();
return request;
}
return null;
}
You are deleting the many side of a 1-to-many relationship there. Everything is working as expected.
Try deleting a Company instead.
I have two classes:
One is User
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<Subscription> Subscriptions { get; set; }
}
Other is Subscription:
public class Subscription
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
As you can see that User has a list of Subscriptions.
Now when using the entity framework code first approach I am getting a table for User which doesn't contain Subscriptions but a new column for User Id is being added to Subscription table. I was expecting to have a third table which contains two columns one with User ID and the other with subscription ID.
How can I achieve this?
From documentation:
Many-to-many relationships without an entity class to represent the join table are not yet supported. However, you can represent a many-to-many relationship by including an entity class for the join table and mapping two separate one-to-many relationships.
So this answer is correct.
I just corrected code a little bit:
class MyContext : DbContext
{
public DbSet<Use> Users { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserSubscription>()
.HasKey(t => new { t.UserId, t.SubscriptionId });
modelBuilder.Entity<UserSubscription>()
.HasOne(pt => pt.User)
.WithMany(p => p.UserSubscription)
.HasForeignKey(pt => pt.UserId);
modelBuilder.Entity<UserSubscription>()
.HasOne(pt => pt.Subscription)
.WithMany(t => t.UserSubscription)
.HasForeignKey(pt => pt.SubscriptionId);
}
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<UserSubscription> UserSubscriptions{ get; set; }
}
public class Subscription
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public List<UserSubscription> UserSubscriptions{ get; set; }
}
public class UserSubscription
{
public int UserId { get; set; }
public User User { get; set; }
public int SubscriptionId { get; set; }
public Subscription Subscription { get; set; }
}
PS. You don't need use virtual in navigation property, because lazy loading still not available in EF Core.
Create a third middle table named: UserSubscriptions for example.
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public virtual ICollection<UserSubscription> Subscriptions { get; set; }
}
public class Subscription
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class UserSubscription
{
public int ID { get; set; }
public int SubscriptionID { get; set; }
public decimal Price { get; set; }
public int UserID { get; set; }
public virtual User { get; set; }
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
}
Second Solution:
Add reference for Subscription to User and name it CurrentSubscription for example.
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int CurrentSubscriptionID { get; set; }
public virtual Subscription Subscription { get; set; }
}
public class Subscription
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
I have 3 classes that must be in a many-to-many relationship, but when I run my project, Entity Framework can't map them to SQL Server tables. I used migrations.
My user class is here:
[Table("tbl_User")]
public class User
{
public User()
{
}
#region Properties
[Key]
[Required][DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string LoginName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public bool EmailVerify { get; set; }
public string Image { get; set; }
public DateTime CreateDate { get; set; }
public DateTime UpdateDate { get; set; }
public DateTime LoginDate { get; set; }
public bool Enabled { get; set; }
public bool Deleted { get; set; }
#endregion
#region Relations
public virtual IList<UserRole> UserRole { get; set; }
#endregion Relations
}
Role class is here:
[Table("tbl_Role")]
public class Role
{
public Role()
{
}
#region Properties
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string RoleName { get; set; }
public int Order { get; set; }
public string Icon { get; set; }
public DateTime CreateDate { get; set; }
public DateTime UpdateDate { get; set; }
public bool Enable { get; set; }
public bool Deleted { get; set; }
#endregion Properties
#region Relations
public virtual IList<UserRole> UserRole { get;set;}
#endregion Relations
}
UserRole is my many-to-many relationship table:
[Table("tbl_UserRole")]
public class UserRole
{
internal class Configuration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<UserRole>
{
public Configuration()
{
HasRequired(current => current.User)
.WithMany(userrole => userrole.UserRole)
.HasForeignKey(fk => fk.UserId);
HasRequired(current => current.Role)
.WithMany(userrole => userrole.UserRole)
.HasForeignKey(fk => fk.RoleId);
}
}
public UserRole()
{
}
#region Properties
[Key]
[Required]
public long Id { get; set; }
public DateTime GrantDate { get; set; }
public DateTime ExpireDate { get; set; }
public bool Enabled { get; set; }
public bool Deleted { get; set; }
#endregion Properties
#region Relations
public long UserId { get; set; }
public virtual User User { get; set; }
public int RoleId { get; set; }
public virtual Role Role { get; set; }
#endregion Relations
}
and here is my database context
public class LiveMiracleDbContext:DbContext
{
public LiveMiracleDbContext() : base("LMConnection")
{
}
public DbSet<User> tbl_User { get; set; }
public DbSet<Role> tbl_Role { get; set; }
public DbSet<UserRole> tbl_UserRole { get; set; }
}
When I run my project without these classes, my database is generated, but when I use these classes, my database is not generated.
ok tanks for Gert Arnold comment
problem was in my Attributes this stracture is true
i removed attributes.
I have few Domain Models - Address, Customer, Employee, StoreLocation. Address has many to one relationship with Customerand Employee and one to one relationship with StoreLocation.
public class Address
{
public int Id;
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class StoreLocation
{
public int Id { get; set; }
public string ShortCode { get; set; }
public string Description { get; set; }
public Address Address { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Dob { get; set; }
public IList<Address> Addresses { get; set; }
}
How to Map this relationship?. I am using ASP.NET MVC 3.0 and Entity Framework 4.1.
If you are using code-first (I think you want this, else, you have to edit your Q), the first way is the way explained below:
Entities:
public class Address {
[Key]
public int Id { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public virtual Customer Customer { get; set; }
public virtual StoreLocation StoreLocation { get; set; }
public virtual Employee Employee { get; set; }
public int? CustomerId { get; set; }
public int? EmployeeId { get; set; }
}
public class Customer {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class StoreLocation {
[Key]
public int Id { get; set; }
public string ShortCode { get; set; }
public string Description { get; set; }
public virtual Address Address { get; set; }
}
public class Employee {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime Dob { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
DbContext inherited class:
public class ManyOneToManyContext : DbContext {
static ManyOneToManyContext() {
Database.SetInitializer<ManyOneToManyContext>(new ManyOneToManyInitializer());
}
public DbSet<Address> Addresses { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<StoreLocation> StoreLocations { get; set; }
public DbSet<Employee> Employees { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Entity<Customer>().HasMany(c => c.Addresses).WithOptional(a => a.Customer).HasForeignKey(a => a.CustomerId);
modelBuilder.Entity<StoreLocation>().HasRequired(s => s.Address).WithOptional(a => a.StoreLocation).Map(t => t.MapKey("AddressId"));
modelBuilder.Entity<Employee>().HasMany(e => e.Addresses).WithOptional(a => a.Employee).HasForeignKey(e => e.EmployeeId);
}
}
Context Initializer:
public class ManyOneToManyInitializer : DropCreateDatabaseAlways<ManyOneToManyContext> {
protected override void Seed(ManyOneToManyContext context) {
}
}
That will create the db-schema below:
Let me know if you have any questions or need clarifications on any part.