I am using EF code first to generate many to many relation. as the following:
public class User
{
public User()
{
Categories = new Collection<Category>();
}
public int UserId { get; set; }
public string LastName { get; set; }
public ICollection<Category> Categories { get; set; }
}
and
public sealed class Category
{
public Category()
{
Users = new Collection<User>();
}
public string Name { get; set; }
public int Id { get; set; }
public ICollection<User> Users { get; set; }
}
My Db context look like:
public DbSet<User> Users { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(b => b.Categories).WithMany(a => a.Users).Map(m => m.MapLeftKey("userId").MapRightKey("id"));
base.OnModelCreating(modelBuilder);
}
The weird issue is when Im doing simple action like:
_ctx.Categories.AsQueryable();
I get the following error:
The constraint 'PK_dbo.Categories' is being referenced by table 'UserCategories', foreign key constraint 'FK_dbo.CategoryUsers_dbo.Categories_id'.
Could not drop constraint. See previous errors.
Related
I want to create a one-to-many relationship using EF 6 using a code-first approach.
Let's take simple and classical example. I have two entities Invoice and UserApplication which have a one-to-many relationship:
I also want to have an UpdatedById relationship with the same ApplicationUser table, to be able to show the names in the UI of who added the record and who modified it.
public partial class ApplicationUser : IdentityUser
{
public string FirstName { get; set; };
public string LastName { get; set; };
}
public virtual List<Invoice> Invoices { get; set; }
public class Invoice
{
public int Id { get; set; }
public string Details { get; set; }
public string CreatedById { get; set; }
public string UpdatedById { get; set; }
}
public virtual ApplicationUser CreatedBy { get; set; }
builder.Entity<Invoice>()
.HasOne(f => f.CreatedBy)
.WithMany(mu => mu.Invoices)
.HasForeignKey(f => f.CreatedById);
If you want Navigation Properties on Application user for these relationships, you would need to create and configure seperate ones.
eg
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System;
using System.Collections.Generic;
namespace EfCore6Test
{
public partial class ApplicationUser //: IdentityUser
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Invoice> InvoicesCreated { get; } = new HashSet<Invoice>();
public virtual ICollection<Invoice> InvoicesLastUpdated { get; } = new HashSet<Invoice>();
}
public class Invoice
{
public int Id { get; set; }
public string Details { get; set; }
public int CreatedById { get; set; }
public int UpdatedById { get; set; }
public virtual ApplicationUser CreatedBy { get; set; }
public virtual ApplicationUser LastUpdatdBy { get; set; }
}
public class Db: DbContext
{
public DbSet<Invoice> Invoices{ get; set; }
public DbSet<ApplicationUser> Users{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Invoice>()
.HasOne(f => f.CreatedBy)
.WithMany(mu => mu.InvoicesCreated)
.HasForeignKey(f => f.CreatedById)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Invoice>()
.HasOne(f => f.LastUpdatdBy)
.WithMany(mu => mu.InvoicesLastUpdated)
.HasForeignKey(f => f.UpdatedById)
.OnDelete(DeleteBehavior.Restrict);
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;database=efCore6Test;Integrated Security=true;TrustServerCertificate=true", o => o.UseRelationalNulls(true))
.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
base.OnConfiguring(optionsBuilder);
}
}
class Program
{
static void Main(string[] args)
{
{
using var db = new Db();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
}
}
}
}
Or simply omit the Navigation Properties on Application User.
I am using EF Core 5 and have extended EF Core Identity to manage user auth. But when I migrate my Entity objects to the database it creates extra columns in the database for example:
AspNetUserRole table creates 4 columns in the database UserId UserId1 RoleId RoleId1 in the database.
Here are my extended classes to give you more insights:
ASP.NET USER ENTITY:
public class AspNetUser : IdentityUser
{
public AspNetUser()
{
UserRoles = new List<AspNetUserRoles>();
AspNetUserShippingInfo = new List<AspNetUserShippingInfo>();
AspNetUserPaymentInfo = new List<AspNetUserPaymentInfo>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string ExternalUserId { get; set; }
public string ExternalProviderName { get; set; }
public bool VerifiedEmail { get; set; }
[ForeignKey("Vendor")]
public int? VendorId { get; set; }
public virtual Vendor Vendor { get; set; }
//TODO: remove from here. doesn't belong here maybe
[ForeignKey("DeliveryCompany")]
public int? DeliveryCompanyId { get; set; }
public virtual DeliveryCompanies DeliveryCompany { get; set; }
public virtual IList<AspNetUserShippingInfo> AspNetUserShippingInfo { get; set; }
public virtual IList<AspNetUserPaymentInfo> AspNetUserPaymentInfo { get; set; }
public virtual IList<AspNetUserRoles> UserRoles { get; set; }
public int? PreferredZipCode { get; set; }
public string PreferredLanuage { get; set; }
}
ASP.NET ROLE ENTITY:
public class AspNetRoles : IdentityRole<string>
{
public AspNetRoles()
{
UserRoles = new List<AspNetUserRoles>();
}
public virtual IList<AspNetUserRoles> UserRoles { get; set; }
}
ASP.NET USER ROLE ENTITY:
public class AspNetUserRoles : IdentityUserRole<string>
{
public AspNetUserRoles() : base()
{
}
public override string UserId { get; set; }
[ForeignKey("UserId")]
public virtual AspNetUser User { get; set; }
public override string RoleId { get; set; }
[ForeignKey("RoleId")]
public virtual AspNetRoles Role { get; set; }
}
I'm not sure what I'm doing wrong that generates these extra columns in the database. any help will be highly appreciated. Thanks
You can do the following step to solve your issue.
1:change your AspNetUserRoles
public class AspNetUserRoles : IdentityUserRole<string>
{
public AspNetUserRoles() : base()
{
}
public override string UserId { get; set; }
public virtual AspNetUser User { get; set; }
public override string RoleId { get; set; }
public virtual AspNetRoles Role { get; set; }
}
2:In your context:
public class ApplicationDbContext : IdentityDbContext<AspNetUser, AspNetRoles, string,
IdentityUserClaim<string>, AspNetUserRoles, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//add this code,do not set a composite primary key.
modelBuilder.Entity<AspNetUser>(b =>
{
b.HasMany(e => e.UserRoles)
.WithOne(e => e.User)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
modelBuilder.Entity<AspNetRoles>(b =>
{
b.HasMany(e => e.UserRoles)
.WithOne(e => e.Role)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
});
}
3:Remigration and update database.
If You Don't Include This Code Then Your Code Works Fine Your AspNetUserRoles Class Look Like This
public class AspNetUserRoles : IdentityUserRole<string>
{
public AspNetUserRoles() : base()
{
}
[Key]
public virtual AspNetUser User { get; set; }
[Key]
public virtual AspNetRoles Role { get; set; }
}
You Can Also Do Something Like This
public class AspNetUser : IdentityUser
{
public AspNetUser()
{
this.Role=new HashSet<AspNetRoles>();
}
public virtual ICollection<AspNetRoles> Role { get; set; }
}
public class AspNetRoles : IdentityRole<string>
{
public AspNetRoles()
{
this.User=new Hashest<AspNetUser>();
}
public virtual ICollection<AspNetUser> User { get; set; }
}
If you Go With Second Option Then You don't Need To add Extra Class AspNetUserRoles for Many To Many Relationship
Just Try This Last code it working Fine in my computer
public class AspNetRoles
{
public string RoleId { get; set; }
public string USerId { get; set; }
}
I have a many to many relationship mapping, and the mapping table has an additional field. I created the ApplicationDbContext as below:
public virtual DbSet<Country> Countries { get; set; }
public virtual DbSet<Region> Regions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CountryRegionMapping>()
.HasKey(um => um.CountryRegionMappingId)
.ToTable("CountryRegionMapping");
modelBuilder.Entity<CountryRegionMapping>()
.HasRequired(um => um.Region).WithMany(g => g.CountryMappings)
.HasForeignKey(um => um.RegionId);
modelBuilder.Entity<CountryRegionMapping>()
.HasRequired(um => um.Country).WithMany(g => g.RegionMappings)
.HasForeignKey(um => um.CountryId);
base.OnModelCreating(modelBuilder);
}
I was refering this link to create the Many-to-Many relationship having an extra field in the mapping table.
The entity classes are :
public class Country
{
public int Id { get; set; }
public string SystemOneName { get; set; }
public string SystemTwoName { get; set; }
public virtual ICollection<CountryRegionMapping> RegionMappings { get; set; }
}
public class Region
{
public int Id { get; set; }
public string SystemOneName { get; set; }
public string SystemTwoName { get; set; }
public virtual ICollection<CountryRegionMapping> CountryMappings { get; set; }
}
public class CountryRegionMapping
{
public int CountryRegionMappingId { get; set; }
public int CountryId { get; set; }
public virtual Country Country { get; set; }
public int RegionId { get; set; }
public virtual Region Region { get; set; }
public bool CheckField { get; set; }
}
When I try to access the Country or Region tables I can simply access it through the code as below using dbcontext.Regions
ApplicationDbContext db = new ApplicationDbContext();
db.Regions.SingleOrDefault(r => r.Id == Id);
But when I try to access the "CountryRegionMapping" entity I cannot access through a code as db.CountryRegionMapping
Why is this not available in the Db Context. How can I access this entity in the middle of a Many-to-Many relationship.
If you want to access the middle-mapping entities directly, add those as a DbSet on your context, along with the other ones:
public virtual DbSet<CountryRegionMapping> CountryRegionMappings { get; set; }
public virtual DbSet<Country> Countries { get; set; }
public virtual DbSet<Region> Regions { get; set; }
Then you can access them same as you access Countries, or Regions:
ApplicationDbContext db = new ApplicationDbContext();
db.CountryRegionMappings//.SingleOrDefault, etc.
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; }
}
Here's the problem. I have table User which have quite a few fields. What I want to do is split this table into multiple entities like this:
User
-> GeneralDetails
-> CommunicationDetails
-> Address
etc.
All goes well when extracting some fields from User into GeneralDetails. However, when I try to do the same thing for CommunicationDetails EF blows up and require to establish one-to-one relationship between GeneralDetails and CommunicationDetails.
Sample entities definition:
public class User {
public int UserId { get; set; }
public string SomeField1 { get; set; }
public int SomeField2 { get; set; }
public virtual GeneralDetails GeneralDetails { get; set; }
public virtual CommunicationDetails CommunicationDetails { get; set; }
public virtual Address Address { get; set; }
}
public class GeneralDetails {
[Key]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public virtual User User { get;set; }
}
public class CommunicationDetails {
[Key]
public int UserId { get; set; }
public string Phone { get; set; }
public string DeviceToken { get; set; }
public virtual User User { get;set; }
}
public class Address {
[Key]
public int UserId { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Street { get; set; }
public virtual User User { get;set; }
}
Sample mapping:
modelBuilder.Entity<User>().
HasRequired(user => user.GeneralDetails).
WithRequiredPrincipal(details => details.User);
modelBuilder.Entity<User>().
HasRequired(user => user.CommunicationDetails).
WithRequiredPrincipal(details => details.User);
modelBuilder.Entity<User>().
HasRequired(user => user.Address).
WithRequiredPrincipal(details => details.User);
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<GeneralDetails>().ToTable("Users");
modelBuilder.Entity<Address>().ToTable("Users");
Why on earth EF want this relationship? Is there any way this could be solved?
The correct way to actually do this is by Complex Types rather than entities. Its actually a more common problem than you think.
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelbuilder.ComplexType<CommunicationDetails>();
modelbuilder.ComplexType<GeneralDetails>();
modelbuilder.ComplexType<Address>();
modelbuilder.Entity<User>().ToTable("Users");
}
}