Code-first database mapping, table missing (wasn't created) for many-to-many relationship - asp.net

I am building ASP.NET webforms application using Entity Framework 6.1, with code-first approach to generate database. I have two tables, Product and Tags, in many-to-many relationship. Classes are below:
public class Product
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products{ get; set; }
}
I want two junction tables out of this relationship ProductTags and ProductTagsTradeFor. So I overrided OnModelCreating of WebsiteDbContext.
modelBuilder.Entity<Product>().HasMany<Tag>(s => s.Tags).WithMany(c => c.Products)
.Map(cs =>
{
cs.MapLeftKey("ProductId");
cs.MapRightKey("TagId");
cs.ToTable("ProductTags");
});
modelBuilder.Entity<Product>().HasMany<Tag>(s => s.Tags).WithMany(c => c.Products)
.Map(cs =>
{
cs.MapLeftKey("ProductId");
cs.MapRightKey("TagId");
cs.ToTable("ProductTradeForTags");
});
After running the application, database was created and table ProductTradeForTags is present but table ProductTags was missing.
What is the problem and how do I fix it so both tables are created?

You can't share the navigation properties. You will need to add a second set of navigation collections to each:
public class Product
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual ICollection<Tag> TradeForTags { get; set; }
}
public class Tag
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products{ get; set; }
public virtual ICollection<Product> TradeForProducts{ get; set; }
}
Then
modelBuilder.Entity<Product>().HasMany(s => s.Tags).WithMany(c => c.Products)
.Map(cs =>
{
cs.MapLeftKey("ProductId");
cs.MapRightKey("TagId");
cs.ToTable("ProductTags");
});
modelBuilder.Entity<Product>().HasMany(s => s.TradeForTags).WithMany(c => c.TradeForProducts)
.Map(cs =>
{
cs.MapLeftKey("ProductId");
cs.MapRightKey("TagId");
cs.ToTable("ProductTradeForTags");
});

Your model require to navigate from a Tag to Products and from a Product to Tags.
In this case one association table is enough.
EF should raise an exception but it simply ignores the first configuration.

Related

Establishing one to one relations with Entity Framework 7

Having the following parent class:
[Table("smart_recharge_registro")]
public class SmartRechargeRegistro
{
[Key]
public int id { get; set; }
public SmartRechargeRequest request { get; set; }
public SmartRechargeProceso proceso { get; set; }
public SmartRechargeResponse response { get; set; }
}
Which in turn references the following child classes:
[Table("smart_recharge_request")]
public class SmartRechargeRequest
{
public String nombreDeUsuario { get; set; }
public String passwordDeUsuario { get; set; }
public String msisdnSuscriptor { get; set; }
}
and:
[Table("smart_recharge_proceso")]
public class SmartRechargeProceso
{
[Key]
public int id { get; set; }
public String carrierId { get; set; }
public String cliente { get; set; }
public String network { get; set; }
}
and lastly:
[Table("smart_recharge_response")]
public class SmartRechargeResponse
{
public Boolean responseSuccess { get; set; }
public int responseCode { get; set; }
public String? responseDetails { get; set; }
}
The Add-Migration and Update-Database command execute without problems. However, when I try to save
await _repository.RegistroColeccion.AddAsync(registro);
await _repositorio.SaveChangesAsync();
I get the following error:
Microsoft.EntityFrameworkCore.DbUpdateException: Could not save changes. Please configure your entity type accordingly.
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Cannot add
or update a child row: a foreign key constraint fails
(beservicebroker_dev.registro_eventos_srdotnet, CONSTRAINT
FK_registro_eventos_srdotnet_SmartRechargeProceso_procesoid FOREIGN
KEY (procesoid) REFERENCES smartrechargeproceso (id) O)
To solve the problem, I tried to create one-to-one relationships following this tutorial
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.request)
.WithOne(r => r.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeRequest>(r => r.id);
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.proceso)
.WithOne(p => p.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeProceso>(p => p.id);
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.response)
.WithOne(r => r.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeResponse>(r => r.id);
Inside SmartRechargeRequest, SmartRechargeProceso and SmartRechargeResponse, added the following:
[JsonIgnore]
public SmartRechargeRegistro SmartRechargeRegistro { get; set; }
Also added inside SmartRechargeRequest and SmartRechargeResponse an id
[Key]
public int id { get; set; }
I'm still unable to test the endpoint because the SmartRechargeRequest and SmartRechargeResponse are completely disfigured in the swagger (even if the [JsonIgnore] or [IgnoreDataMember] annotations are set) due to the presence of that SmartRechargeRegistro object.
I'm pretty sure my solution is misguided and I'm getting the process completely wrong.
What would be the proper way to map one-to-one relationships for this case? Any help will be appreciated.
Please note that in reality, these classes are huge (dozens of properties), so it's not possible to merge all of them on a single table.

Multiple (optional) one to one Relationships with single navigation property

I'm trying to configure a relationship between two entities where the parent can have multiple (named) childs from the same type while the child should only have one generic parent.
Based on the Blog/Posts example I'm trying to configure the following. Blogposts aren't a good example but in my application I have 5 fixed child entities.
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int? FirstPostId { get; set; }
public Post FirstPost { get; set; }
public int? SecondPostId { get; set; }
public Post SecondPost { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int? BlogId { get; set; }
public Blog Blog { get; set; }
}
I tried to configure this like:
modelBuilder
.Entity<Blog>()
.HasOne(_ => _.FirstPost)
.WithOne()
.HasForeignKey<Blog>(_ => _.FirstPostId);
modelBuilder
.Entity<Blog>()
.HasOne(_ => _.SecondPost)
.WithOne()
.HasForeignKey<Blog>(_ => _.SecondPostId);
modelBuilder
.Entity<Post>()
.HasOne(_ => _.Blog)
.WithOne()
.HasForeignKey<Post>(_ => _.BlogId);
In my real world problem a Post additionally does not need to belong to a Blog and all foreign keys are composite keys but that should be no problem.
Configured like that FirstPost and SecondPost are recognized but Post.Blog/Post.BlogId is always null.
Any ideas how to solve this?

EFCore Many-To-Many relationship with the same table on the ends

I want to track referrals for users. There are two entities classes.
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public ICollection<Referral> Referrals { get; set; }
public ICollection<Referral> Referrees { get; set; }
}
public class Referral
{
public long ReferrerId { get; set; }
public long ReferreeId { get; set; }
public virtual User Referrer { get; set; }
public virtual User Referree { get; set; }
}
I defined relationships between them through fluent API on model creating (for the Referral entity)
builder.HasOne(r => r.Referrer).WithMany(u => u.Referrals).HasForeignKey(r => r.ReferrerId);
builder.HasOne(r => r.Referree).WithMany(u => u.Referrees).HasForeignKey(u => u.ReferreeId);
But when I try adding a migration, I get the
Unable to determine the relationship represented by navigation
property 'Referral.Referrer' of type 'User'. Either manually configure
the relationship, or ignore this property using the '[NotMapped]'
attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
error.
What am I doing wrong?

How to define two FK in the same table?

I have a table called User which inherit the properties from IdentityUser, inside that table I added a reference to the UserFriendship table which need to store all the user friendship:
public class User : IdentityUser
{
public string FirstName { get; set; }
public DateTime BirthDate { get; set; }
public virtual ICollection<UserFriendship> UserFriendship { get; set; }
}
Essentially the UserFriendship contains two users, who are those who have a common friendship, this is the model definition:
public class UserFriendship
{
public int Id { get; set; }
[Key, ForeignKey("UserA")]
public string UserAId { get; set; }
public User UserA { get; set; }
[Key, ForeignKey("UserB")]
public string UserBId { get; set; }
public User UserB { get; set; }
[Required]
public DateTime DateTime { get; set; }
}
I defined the UserA and the UserB which are two FK of a User that are contained inside AspNetUsers table.
Now inside the FluentAPI I declared the following:
builder.Entity<UserFriendship>(entity =>
{
entity.HasKey(f => f.Id);
entity.HasOne(u => u.UserA)
.WithMany(n => n.UserFriendships)
.HasForeignKey(u => u.UserAId)
.IsRequired();
entity.HasOne(u => u.UserB)
.WithMany(n => n.UserFriendships)
.HasForeignKey(u => u.UserBId)
.IsRequired();
});
when I execute this command:
add-migration InitialMigration -context MyAppContext
I'll get:
Cannot create a relationship between 'User.UserFriendships' and 'UserFriendship.UserB', because there already is a relationship between 'User.UserFriendships' and 'UserFriendship.UserA'. Navigation properties can only participate in a single relationship.
I'm not an expert of EnityFramework, but based on that error I think that I cannot define two FK in the same table?
Sorry for any mistake, thanks.
You can define more than one FK in table.
The problem here is you are pointing two times to one navigation property - UserFriendships. The solution would be to create two navigation properties.
Navigation properties are used to browse the related data for specified foreign-key (you have one-to-many relationship) of entity.
Try this:
public class User
{
public string FirstName { get; set; }
public DateTime BirthDate { get; set; }
public ICollection<UserFriendship> UserAFriendships { get; set; }
public ICollection<UserFriendship> UserBFriendships { get; set; }
}
public class UserFriendship
{
public int Id { get; set; }
public string UserAId { get; set; }
public User UserA { get; set; }
public string UserBId { get; set; }
public User UserB { get; set; }
public DateTime DateTime { get; set; }
}
And define the relationship through fluent api as following:
modelBuilder.Entity<UserFriendship>(entity =>
{
entity.HasKey(f => f.Id);
entity.HasOne(u => u.UserA)
.WithMany(n => n.UserAFriendships)
.HasForeignKey(u => u.UserAId)
.IsRequired();
entity.HasOne(u => u.UserB)
.WithMany(n => n.UserBFriendships)
.HasForeignKey(u => u.UserBId)
.IsRequired();
});
What is more - you don't need to specify attributes Key, ForeignKey if you use Fluent API.

ASP.NET Core 2.0 MVC EF One-to-Many and Many-to-Many relationship

In the last days I have been working on the creation of a relationship between different classes. Unfortunately, I can't solve it, hopefully you can help me further. Below you can find the problem definition.
I have the following classes:
BaseEntity
Product
Category
Tag
ProductCategory
ProductTag
BaseEntity Class:
public class BaseEntity
{
public long Id { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public BaseEntity { }
}
Product Class:
public class Product: BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public long CategoryId { get; set; }
public virtual Category MainCategory { get; set; }
public virtual ICollection<ProductSubCategory> ProductSubCategories { get; set; }
public virtual ICollection<ProductTag> ProductTags { get; set; }
}
Category Class:
public class Category : BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<ProductSubCategory> ProductSubCategories{ get; set; }
}
Tag Class:
public class Tag: BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductTag> ProductTags { get; set; }
}
ProductSubCategory Class:
public class ProductSubCategory : BaseEntity
{
public long ProductId { get; set; }
public Product Product{ get; set; }
public long SubCategoryId { get; set; }
public Category Category { get; set; }
}
ProductTag Class:
public class ProductTag: BaseEntity
{
public long ProductId { get; set; }
public Product Product{ get; set; }
public long TagId { get; set; }
public Tag Tags { get; set; }
}
A product has one main category. (one-to-many)
A product could have many subcategories. (many-on-many)
A product could have many tags. (many-on-many)
My class-Maps are like this:
ProductMap:
public ProductMap(EntityTypeBuilder<Product> entityBuilder)
{
entityBuilder.HasOne(p => p.MainCategory)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId)
.OnDelete(DeleteBehavior.SetNull);
}
ProductSubCategoryMap:
public ProductSubCategoryMap(EntityTypeBuilder<ProductSubCategory> entityBuilder)
{
entityBuilder
.HasKey(psc=> new { psc.ProductId, psc.CategoryId });
entityBuilder
.HasOne<Product>(psc => psc.Product)
.WithMany(p => p.ProductSubCategories)
.HasForeignKey(psc => psc.ProductId);
entityBuilder
.HasOne<Category>(psc=> psc.Category)
.WithMany(sc => sc.ProductSubCategories)
.HasForeignKey(psc => psc.CategoryId);;
}
ProductTagMap:
public ProductTagMap(EntityTypeBuilder<ProductTag> entityBuilder)
{
entityBuilder
.HasKey(pt => new { pt.ProductId, pt.TagId});
entityBuilder
.HasOne<Product>(pt => pt.Product)
.WithMany(p => p.ProductTags)
.HasForeignKey(pt=> pt.ProductId);
entityBuilder
.HasOne<Tag>(pt => pt.Tag)
.WithMany(t => t.ProductTags)
.HasForeignKey(pt => pt.TagId);
}
How can I set:
One-to-many between Product(MainCategory) and Category.
Many-on-many between Product(SubCategories) and Category
Many-on-many between Product(Tags) and Tag.
Thanks in advance.
A few questions first, you are using code-first approach yes? As you are using the virtual navigation properties. Just want to be sure.
Are you currently getting any errors, what have you tried, what is failing?
Where you place the virtual property and not in the other model, that will give you a one-to-many relationship. If they are in both models you achieve the many-to- many relationship.
In Product class add
public virtual Catagory Catagory { get; set; }
If you need it to be a collection then add that before like you have in your others and maybe name the ICollection Catagories instead of singular .
public virtual ICollection<Catagory> Catagories { get; set; }

Resources