Work with many-to many relationships C# - c#

Good evening!
I use Asp.net MVC with EF.
I have 2 models
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Category> categories { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Product> products { get; set; }
}
many-to many add normaly work, if I add new product, but if i try update product entity,new relationship doesnt add.
I add new products like this
Product product= new Product{...};
product.categories.Add(db.Categories.First())//example
How I can add/delete relationships in product entity update?

First, your ICollection<T> should be a virtual property:
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
}
Next, create an association table between the two:
public class ProductCategory
{
public virtual Product Product { get; set; }
public virtual Category Category { get; set; }
}
Then in your dbContext, add the relationships:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ProductCategory>()
.HasKey(c => new { c.CategoryId, c.ProductId });
modelBuilder.Entity<Product>()
.HasMany(c => c.ProductCategories)
.WithRequired()
.HasForeignKey(c => c.ProductId);
modelBuilder.Entity<Category>()
.HasMany(c => c.ProductCategories)
.WithRequired()
.HasForeignKey(c => c.CategoryId);
}

Related

How to do Many To Many relationship in Ef Core using Fluent API?

I'm trying to create a many to many relation in the Fluent API with Ef Core 6 but i am having trouble understanding how to do so.
I've looked around here in stackoverflow but couldn't understand this relation and how to reproduce it in my code.
I have a table in my SQL database called People:
People.cs:
public class People : PeopleBase
{
public People()
{
RegistrationList = new HashSet<Registration>();
}
public virtual ICollection<Registration> RegistrationList { get; set; }
public virtual User User { get; set; }
public virtual ActivityGroup ActivityGroup { get; set; }
}
PeopleBase.cs:
public abstract class PeopleBase: ModelBase
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid PeopleId { get; set; }
public Guid? UserId { get; set; }
public Guid? ActivityGroupId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public PeopleActiveType Active { get; set; }
}
And then i have another table called ActivityGroup:
ActivityGroup.cs:
public class ActivityGroup : ActivityGroupBase
{
public ActivityGroup()
{
PeopleList = new HashSet<People>();
ActivityList = new HashSet<Activity>();
}
public ICollection<People> PeopleList { get; set; }
public ICollection<Activity> ActivityList { get; set; }
}
ActivityGroupBase.cs:
public abstract class ActivityGroupBase : ModelBase
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ActivityGroupId { get; set; }
[Required]
[StringLength(20)]
public string Name { get; set; }
public StatusRecord Status { get; set; }
}
How would i do the mapping in the modelBuilder given that:
ActivityGroupId is the foreing key in the People database, pointing to the other table
One PeopleId can have multiple (many) ActivityGroupId
One ActivityGroupId can be assigned to multiple people.
What i've done so far:
modelBuilder.Entity<People>()
.HasOne(x => x.ActivityGroup)
.WithMany(x => x.PeopleList)
.HasForeignKey(x => x.ActivityGroupId);
Wouldn't i have to do this instead?
modelBuilder.Entity<People>()
.Hasmany(x => x.ActivityGroupList) //this is a ICollection<ActivityGroup> inside People class
.WithMany(x => x.PeopleList)
.HasForeignKey(x => x.ActivityGroupId); // this is not recognized by Ef Core
Can anyone help me please?
There are two main approaches for many-to-many relationships - with implicit junction table:
public class People : PeopleBase
{
// ...
public virtual List<Activity> Activities { get; set; }
}
public class Activity // : ...
{
// ...
public virtual List<People> PeopleList { get; set; }
}
modelBuilder.Entity<People>()
.HasMany(x => x.Activities)
.WithMany(x => x.PeopleList);
Or with explicit one:
public class People
{
// ...
public ICollection<PeopleActivity> PeopleActivities { get; set; }
}
public class Activity
{
// ...
public virtual ICollection<PeopleActivity> PeopleActivities { get; set; }
}
public class PeopleActivity
{
public Guid ActivityId { get; set; }
public Guid PeopleId { get; set; }
public Activity Activity { get; set; }
public People People { get; set; }
}
modelBuilder.Entity<PeopleActivity>()
.HasOne(pt => pt.People)
.WithMany(t => t.PeopleActivities)
.HasForeignKey(pt => pt.PeopleId);
Also maybe it worth changing entity name from People to Person (you can change table name with .ToTable("People") call)?

EF ... Unwanted duplicate values of two fields

I have two entity for save Product and Product's Related Products ... there is One to Many Relationship ... Everything is right before storing the information But in the database two duplicate fields are stored
RelatedProducID
ProductID
public class RelatedCatalogs : EntityBase
{
public Guid ProductID { get; set; }
public Guid RelatedProducID { get; set; }
public Product RelatedProductCatalog { get; set; }
public int Priority { get; set; }
}
Product Class:
public class Product{
public Guid ProductID { get; set; }
public string Name { get; set; }
[ForeignKey("RelatedProductID")]
public virtual List<RelatedCatalogs> RelatedCatalogs { get; set; }
.
.
.
}
What needs to be done now to fix this problem?
Your problem seems similar to this, so try:
public class Product
{
public Guid ProductID { get; set; }
public string Name { get; set; }
...
[InverseProperty("Product")]
public virtual List<RelatedCatalogs> RelatedCatalogs { get; set; }
}
public class RelatedCatalogs : EntityBase
{
public Guid ID { get; set; }
[ForeignKey("Product")]
public Guid ProductID { get; set; }
public Product Product { get; set; }
[ForeignKey("RelatedProductCatalog")]
public Guid RelatedProductID { get; set; }
public Product RelatedProductCatalog { get; set; }
public int Priority { get; set; }
}
Then you can add the mentioned fluent code to avoid cycles:
modelBuilder.Entity<RelatedCatalogs>()
.HasRequired(r => r.RelatedProductCatalog)
.WithMany()
.HasForeignKey(r => r.RelatedProductID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<RelatedCatalogs>()
.HasRequired(r => r.Product)
.WithMany(p => p.RelatedCatalogs)
.HasForeignKey(r => r.ProductID);

EF self one-to-many relationship

I want to implement one-to-many relationship with EF6. Table User can handle many Friends, i can implement that with map table:
--TABLE USERS:
Id
--TABLE USER_MAP:
UserOwnerId
UserFriendId
But how to implement that with EF6?
Here is my entity User:
public class User
{
...
public virtual List<User> Friends { get; set; }
...
}
You can use something like this
// Relationships
HasRequired(t => t.User)
.WithMany(t => t.Friends)
.HasForeignKey(d => d.UserId);
https://msdn.microsoft.com/en-us/data/hh134698.aspx
One-to-Many relationship using DataAnnotations:
public class User
{
public User() { }
public int UserId { get; set; }
public string Name { get; set; }
public virtual Friends friends { get; set; }
}
public class Friends
{
public Friends()
{
Users = new List<User>();
}
public int FriendId { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
you can defined in Code first like that:
1) Fluent API:
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual Standard Standard { get; set; }
}
public class Standard
{
public Standard()
{
Students = new List<Student>();
}
public int StandardId { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Fleut Api:
in your DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//one-to-many
modelBuilder.Entity<Student>()
.HasRequired<Standard>(s => s.Standard)
.WithMany(s => s.Students);
}
virtual keyword is only for Lazy loading you can remove it if you do not need it
2) Code first:
public class Student
{
public Student()
{
Students= new List<Student>();
}
public int StundendId{ get; set; }
public string StudentName { get; set; }
public int? SharedStudentId{ get; set; }
[ForeignKey("SharedStudentId")]
public Student SharedStudent{ get; set; }
public virtual ICollection<Student> SharedStudents{ get; set; }
}

MVC5 Code First One-To-One Relationship Error

I'm trying to do one-one relationship for MVC5 codefirst. I've looked this page and did exactly same things but I've got an error.
Here is my classes and context:
Product:
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public string ProductDescription { get; set; }
/// <summary>
/// Display order
/// </summary>
public int Order { get; set; }
public string TitleBackgroundColor { get; set; }
public virtual TblClass TblClass { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public virtual ICollection<Price> Prices { get; set; }
public virtual ICollection<ProductFeature> ProductFeatures { get; set; }
}
TblClass:
public class TblClass
{
[Key, ForeignKey("Product")]
public int ProductId { get; set; }
public string ClassName { get; set; }
public virtual ICollection<Permission> Permissions { get; set; }
public virtual Product Product { get; set; }
public int ClassOrder { get; set; }
}
DBContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Role>()
.HasMany<UserProfile>(r => r.UserProfiles)
.WithMany(u => u.Roles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("RoleId");
m.MapRightKey("UserId");
});
modelBuilder.Entity<TblClass>()
.HasKey(c => c.ProductId);
modelBuilder.Entity<Product>()
.HasOptional(f => f.TblClass)
.WithRequired(s => s.Product)
.Map(t => t.MapKey("ProductId"));
}
And when I try to run 'update-database -verbose' I've got this error:
The navigation property 'Product' declared on type 'YazililarGaranti.Domain.Entities.TblClass' has been configured with conflicting foreign keys.
You do not have to use .Map(t => t.MapKey("ProductId") when making an 1:1 relationship. This should work:
public class Product
{
public int ProductId { get; set; }
//... other properties
public virtual TblClass TblClass { get; set; }
//... other properties
}
public class TblClass
{
//[Key, ForeignKey("Product")] <-- remove these attributes
public int ProductId { get; set; }
public string ClassName { get; set; }
//.. other properties
public virtual Product Product { get; set; }
public int ClassOrder { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//other mappings
modelBuilder.Entity<TblClass>()
.HasKey(c => c.ProductId);
modelBuilder.Entity<Product>()
.HasKey(c => c.ProductId); //consider to use database generated option
//modelBuilder.Entity<Product>().Property(t => t.ProductId)
//.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Product>()
.HasOptional(f => f.TblClass)
.WithRequired(s => s.Product);
}
Hope this helps!

How to create a many-to-many mapping in Entity Framework?

Here is the case, I have 2 entities, such as Contract、Media。
public class Media : Entity
{
public string Name {get; set;}
public bool Enabled
*//other properties can be ignored..*
}
public class Contract : Entity
{
public string Code {get; set;}
*//other properties can be ignored..*
}
Contract has many Medias, it seems that they are many to many.
But!! at ef code first, i need 3 more fields in the ContractMedia table(ef auto generated).
such as StartDate,EndDate and Price. these could not be added in Media entity.
How to map at this case??
If you want to create many to many relationship with additional data in association table, you have to make the association table as entity. The pure many to many relationship is only in pure table with entity id's.
In you case it will be:
public class Media // One entity table
{
public int Id { get; set; }
public string Name { get; set; }
public bool Enabled { get; set; }
public virtual ICollection<ContractMedia> ContractMedias { get; set; }
}
public class Contract // Second entity table
{
public int Id { get; set; }
public string Code { get; set }
public virtual ICollection<ContractMedia> ContractMedias { get; set; }
}
public class ContractMedia // Association table implemented as entity
{
public int MediaId { get; set; }
public int ContractId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public double Price { get; set; }
public virtual Media Media { get; set; }
public virtual Contract Contract { get; set; }
}
And after you created models/entities, you need to define relationships in context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ContractMedia>()
.HasKey(c => new { c.MediaId, c.ContractId });
modelBuilder.Entity<Contract>()
.HasMany(c => c.ContractMedias)
.WithRequired()
.HasForeignKey(c => c.ContractId);
modelBuilder.Entity<Media>()
.HasMany(c => c.ContractMedias)
.WithRequired()
.HasForeignKey(c => c.MediaId);
}
Also you can refer to these links:
Many to many mapping with extra fields in Fluent API
Entity Framework CodeFirst many to many relationship with additional information
Create code first, many to many, with additional fields in association table
Adding to #Tomas answer without having to use Fluent API.
public class Media // One entity table
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ContractMedia> ContractMedias { get; set; }
}
public class Contract // Second entity table
{
public int Id { get; set; }
public string Code { get; set }
public virtual ICollection<ContractMedia> ContractMedias { get; set; }
}
public class ContractMedia // Association table implemented as entity
{
[Key]
[Column(Order = 0)]
[ForeignKey("Media")]
public int MediaId { get; set; }
[Key]
[Column(Order = 1)]
[ForeignKey("Contract")]
public int ContractId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public double Price { get; set; }
public virtual Media Media { get; set; }
public virtual Contract Contract { get; set; }
}
EF Core needs to use Fluent API but it would look like this:
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Source:
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration

Categories