In relation 1 - 1, I have two foreign keys in each of tabels that should tell me to what object I', referring. In table "WebAppUser" foreign key works correctly but in table "UserPermissions" I always get 0 as id of foreign key (column: "WebAppUserId" which is reference to a specific "WebAppUser").
My Code:
public class UserPermissions
{
[Key]
public int UserPermissionsId { get; set; }
//public int? WebAppUserId { get; set; }
public int WebAppUserId { get; set; }
public virtual WebAppUser WebAppUser { get; set; }
/*public virtual IEnumerable<WebAppUserClaims> Claims { get; set; }
public virtual IEnumerable<WebAppUserLogin> Logins { get; set; }
public virtual IEnumerable<WebAppUserToken> Tokens { get; set; }*/
public virtual IEnumerable<WebAppUserRole> WebAppUserRoles { get; set; }
}
public class WebAppUser /*: IdentityUser<int>*/
{
[Key]
public int Id { get; set; }
//1:1
public int UserProfileId { get; set; }
public virtual UserProfile UserProfile { get; set; }
//1:1
public int UserCredentialsId { get; set; }
public virtual UserCredential Credentials { get; set; }
//1:1
public int PermissionsId { get; set; }
public virtual UserPermissions UserPermissions { get; set; }
}
//WebAppUser - UserPermissions 1-1
modelBuilder.Entity<WebAppUser>()
.HasOne(x => x.UserPermissions)
.WithOne(y => y.WebAppUser)
.HasForeignKey<WebAppUser>(x => x.PermissionsId);
I tried entity configuration like this, but it also doesn't work:
modelBuilder.Entity<WebAppUser>()
.HasOne(x => x.UserPermissions)
.WithOne(y => y.WebAppUser)
.HasForeignKey<WebAppUser>(x => x.PermissionsId);
modelBuilder.Entity<UserPermissions>()
.HasOne(x => x.WebAppUser)
.WithOne(y => y.UserPermissions)
.HasForeignKey<UserPermissions>(x => x.WebAppUserId);```
Try this change in fluentApi
modelBuilder.Entity<WebAppUser>()
.HasOne<UserPermissions>(x => x.UserPermissions)
.WithOne(y => y.WebAppUser)
.HasForeignKey<UserPermissions>(x => x.WebAppUserId);
And don't forget when you make a request to this table to use Include(x => x.UserPermissions)
Related
I appear to have duplicate foreign keys in my GroceryItemGroceryStores many to many join table: VeganItemId, VeganItemsId, EstablishmentId, EstablishmentsId.
I'm only actually using VeganItemId and EstablishmentId and they are the only ones being added to. It is adding VeganItemsId and EstablishmentsId columns to my table automatically. How do I tell it not to?:
This image of my database shows the foreign keys in effect:
My DatabaseContext:
modelBuilder.Entity<GroceryItem>(gi =>
{
gi.HasIndex(e => new { e.Brand, e.Name }).IsUnique();
gi.HasKey(e => e.Id);
gi.Property(e => e.Tags)
.HasConversion(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<List<GroceryItemTag>>(v, null),
new ValueComparer<IList<GroceryItemTag>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (IList<GroceryItemTag>)c.ToList()));
});
modelBuilder.Entity<GroceryStore>(gs =>
{
gs.HasIndex(gs => gs.PlaceId).IsUnique();
gs.HasMany(gs => gs.VeganItems)
.WithMany(vi => vi.Establishments)
.UsingEntity<GroceryItemGroceryStore>
(gigs => gigs.HasOne<GroceryItem>().WithMany(),
gigs => gigs.HasOne<GroceryStore>().WithMany());
});
modelBuilder.Entity<GroceryItemGroceryStore>(gigs =>
{
gigs.HasIndex(e => new { e.VeganItemId, e.EstablishmentId }).IsUnique();
gigs.HasKey(e => new { e.VeganItemId, e.EstablishmentId });
});
public DbSet<GroceryItem> GroceryItems { get; set; }
public DbSet<GroceryItemGroceryStore> GroceryItemGroceryStores { get; set; }
public DbSet<GroceryStore> GroceryStores { get; set; }
My tables:
public class GroceryStore
{
[Key]
public Int64 Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string PlaceId { get; set; }
[Required]
public string Street { get; set; }
public string Suburb { get; set; }
public string City { get; set; }
public string StreetNumber { get; set; }
public virtual ICollection<GroceryItem> VeganItems { get; set; }
}
public class GroceryItem
{
[Key]
public Int64 Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public string Image { get; set; }
[Required]
public int IsNotVeganCount { get; set; }
[Required]
public int IsVeganCount { get; set; }
[Required]
public int RatingsCount { get; set; }
[Required]
public int Rating { get; set; }
[Required]
public List<GroceryItemTag> Tags { get; set; }
[Required]
public int CurrentRevisionId { get; set; }
public virtual ICollection<GroceryStore> Establishments { get; set; }
}
public class GroceryItemGroceryStore
{
[Key]
public Int64 Id { get; set; }
[Key, Column(Order = 0), Required]
public int VeganItemId { get; set; }
[Key, Column(Order = 1), Required]
public int EstablishmentId { get; set; }
public virtual GroceryItem VeganItem { get; set; }
public virtual GroceryStore Establishment { get; set; }
[Required]
public int NotInEstablishmentCount { get; set; }
[Required]
public int InEstablishmentCount { get; set; }
[Required]
public double Price { get; set; }
}
Even adding .HasForeignKey(o => o.VeganItemId) like so:
modelBuilder.Entity<GroceryStore>(gs =>
{
gs.HasIndex(gs => gs.PlaceId).IsUnique();
gs.HasMany(gs => gs.VeganItems)
.WithMany(vi => vi.Establishments)
.UsingEntity<GroceryItemGroceryStore>
(gigs => gigs.HasOne<GroceryItem>().WithMany().HasForeignKey(o => o.EstablishmentId),
gigs => gigs.HasOne<GroceryStore>().WithMany().HasForeignKey(o => o.VeganItemId));
});
Makes the table have VeganItemId1 and EstablishmentId1:
EDIT: I have since tried deleting all occurrences of VeganItemsId and EstablishmentsId from all migration files but when inserting to the database it still thinks it needs to insert EstablishmentsId.
In GroceryItemGroceryStore change the type of VeganItemId and EstablishmentId to Int64 so that they match the type of the corresponding primary keys in GroceryItem and GroceryStore -
[Column(Order = 0), Required]
public Int64 VeganItemId { get; set; } // Key attribute is not needed here
[Column(Order = 1), Required]
public Int64 EstablishmentId { get; set; } // Key attribute is not needed here
Modify the configuration for GroceryStore to include the navigation properties and to explicitly configure the foreign keys -
builder.Entity<GroceryStore>(gs =>
{
gs.HasIndex(gs => gs.PlaceId).IsUnique();
gs.HasMany(gs => gs.VeganItems)
.WithMany(vi => vi.Establishments)
.UsingEntity<GroceryItemGroceryStore>
(gigs => gigs.HasOne(p => p.VeganItem)
.WithMany().HasForeignKey(p => p.VeganItemId),
gigs => gigs.HasOne(p => p.Establishment)
.WithMany().HasForeignKey(p => p.EstablishmentId));
});
That should fix the duplicate keys issue.
Also, you should remove the following property from GroceryItemGroceryStore -
[Key]
public Int64 Id { get; set; }
since you are configuring a composite primary key through fluent API.
The presence of such FKs is a clear indication of relationship misconfiguration and usually happens when you leave out from the fluent configuration some of the relationship navigation properties, in which case EF maps them to a separate FK relationship with conventional FK property/column names.
In this particular case, the misconfiguration is here
gigs => gigs.HasOne<GroceryItem>().WithMany() // (1)
and
gigs.HasOne<GroceryStore>().WithMany() // (2)
because you have left out the navigation properties of GroceryItemGroceryStore
public virtual GroceryItem VeganItem { get; set; } // (1)
public virtual GroceryStore Establishment { get; set; } // (2)
May be they wasn't there initially and you have added them later. But you should always keep the fluent configuration in sync with the model, which in this case of course should be something like
gigs => gigs.HasOne(e => e.VeganItem).WithMany()
and
gigs.HasOne(e => e.Establishment).WithMany()
Your issue is that you have defined VeganItemId and EstablishmentId as part of athe composite Primary Key of GroceryItemGroceryStore, in conjunction with the Artificitial key of Id
It is not necessary and even counter productive to define a composite key that includes a column that is already unique for all rows in the table, if there is a single column that is unique for all records, and you were going to include it in the primary key, then you should just use that column as the PK.
Your fluent configuration is conflicting with your attribute configuration, I would suggest the following is at least part of the solution:
public class GroceryItemGroceryStore
{
[Key]
public Int64 Id { get; set; }
[ForeignKey(nameof(VeganItem)), Column(Order = 0), Required]
public int VeganItemId { get; set; }
[ForeignKey(nameof(Establishment)), Column(Order = 1), Required]
public int EstablishmentId { get; set; }
public virtual GroceryItem VeganItem { get; set; }
public virtual GroceryStore Establishment { get; set; }
[Required]
public int NotInEstablishmentCount { get; set; }
[Required]
public int InEstablishmentCount { get; set; }
[Required]
public double Price { get; set; }
}
Worked a lot with EF 6.x (via designer) and now started on a new project using EF Core.
I'm getting an error that says value cannot be null, not sure exactly what I'm doing wrong. I've got rid of a lot of fields for brevity as there are hundreds.
All these tables are views via synonyms that connect to a different database. I can get it to work fine, if I do each individual call to a database, but as soon as I do include. I get an error on that line. The error I'm getting is
ArgumentNullException: Value cannot be null.
Parameter name: key
System.Collections.Generic.Dictionary.FindEntry(TKey key)
OnGetAsync
var equipment = _context.EMEMs.Include(x => x.EMEDs).Where(x => x.KeyID.ToString() == key);
EMEM = await equipment.Include(x => x.EMCM).ThenInclude(x=>x.EMCDs).FirstOrDefaultAsync();
EMEM
public class EMEM
{
public byte? EMCo { get; set; }
[Display(Name = "Equipment Code")]
public string Equipment { get; set; }
public string Type { get; set; }
public string Category { get; set; }
public Guid? UniqueAttchID { get; set; }
[Key]
public long KeyID { get; set; }
[NotMapped] public string EquipmentDetails => $"{Equipment.Trim()} - {Description} - {VINNumber}";
public virtual IEnumerable<EMWH> EMWHs { get; set; }
public virtual EMCM EMCM { get; set; }
public virtual IEnumerable<udEMED> EMEDs { get; set; }
}
EMCM
public class EMCM
{
[Key]
public long KeyID { get; set; }
public byte? EMCo { get; set; }
public string Category { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public virtual IEnumerable<EMEM> EMEMs { get; set; }
public virtual IEnumerable<udEMCD> EMCDs { get; set; }
}
udEMCD
public class udEMCD
{
[Key]
public long KeyID { get; set; }
public byte? Co { get; set; }
public string Category { get; set; }
public string DocumentCategory { get; set; }
public int Seq { get; set; }
public Guid? UniqueAttchID { get; set; }
public virtual udEMDC EMDC { get; set; }
public virtual EMCM EMCM { get; set; }
public virtual IEnumerable<HQAT> HQATs { get; set; }
}
Context
modelBuilder.Entity<EMEM>().ToTable("EMEM").HasOne(x => x.EMCM).WithMany(x => x.EMEMs).HasForeignKey(x => new { x.EMCo, x.Category }).HasPrincipalKey(x => new { x.EMCo, x.Category });
modelBuilder.Entity<EMEM>().ToTable("EMEM").HasMany(x => x.EMEDs).WithOne(x => x.EMEM).HasForeignKey(x => new { x.Co, x.Equipment }).HasPrincipalKey(x => new { x.EMCo, x.Equipment });
modelBuilder.Entity<EMCM>().ToTable("EMCM").HasMany(x => x.EMCDs).WithOne(x => x.EMCM)
.HasForeignKey(x => new { x.Co, x.Category }).HasPrincipalKey(x => new { x.EMCo, x.Category });
modelBuilder.Entity<udEMCD>().ToTable("udEMCD").HasOne(x => x.EMDC).WithMany(x => x.EMCDs)
.HasForeignKey(x => x.DocumentCategory).HasPrincipalKey(x => x.Category);
modelBuilder.Entity<udEMDC>().ToTable("udEMDC").HasMany(x => x.EMEDs).WithOne(x => x.EMDC).HasForeignKey(x => new{ x.DocumentCategory}).HasPrincipalKey(x => new{ x.Category});
modelBuilder.Entity<udEMED>().ToTable("udEMED");
modelBuilder.Entity<EMWH>().ToTable("EMWH");
modelBuilder.Entity<EMWI>().ToTable("EMWI");
modelBuilder.Entity<HQAT>().HasOne(x => x.EMWH).WithMany(x => x.HQATs).HasForeignKey(x => x.UniqueAttchID)
.HasPrincipalKey(x => x.UniqueAttchID);
modelBuilder.Entity<EMWH>().HasOne(x => x.EMEM).WithMany(x => x.EMWHs)
.HasForeignKey(x => new {x.EMCo, x.Equipment}).HasPrincipalKey(x => new {x.EMCo, x.Equipment});
EDIT: I added nullable KeyID's just to test prior to uploading and still didn't work.
I think the error is that you're declaring the Key as nullable, which it should never happen.
[Key]
public long? KeyID { get; set; }
change your code to this...
[Key]
public long KeyID { get; set; }
I'm trying to create a model with a many-to-many relation:
A Company can have 0 or more Users
A Company can have 0 or more Roles
The many to many relation is between Users and Roles
Every time i try to create the database (update-database) I have the message:
Introducing FOREIGN KEY constraint 'FK_UsersRoles_Users_UserId' on table 'UsersRoles' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
and I don't understand what cycles it refers to. I mean, if I deleted a user it has to delete the entity inside UsersRoles and nothing more. The same if I delete a Role.
If my model has only Users and Roles, without the Company table, the database is created.
What am I doing wrong?
public class MyContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server = (localdb)\\mssqllocaldb; Database = ConsoleApp1; Trusted_Connection = True; MultipleActiveResultSets = true");
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserRole>()
.ToTable("UsersRoles")
.HasKey(t => new { t.UserId, t.RoleId });
modelBuilder.Entity<UserRole>()
.HasOne(pt => pt.User)
.WithMany(p => p.UsersRoles)
.HasForeignKey(pt => pt.UserId);
modelBuilder.Entity<UserRole>()
.HasOne(pt => pt.Role)
.WithMany(t => t.UsersRoles)
.HasForeignKey(pt => pt.RoleId);
base.OnModelCreating(modelBuilder);
}
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<User> Users { get; set; }
public ICollection<Role> Roles { get; set; }
}
public class User
{
public int Id { get; set; }
public string Surname { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
public ICollection<UserRole> UsersRoles { get; set; }
}
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
public ICollection<UserRole> UsersRoles { get; set; }
}
public class UserRole
{
public int UserId { get; set; }
public User User { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
}
I have a table called UsersInRoles with 2 X foreign keys as a composite key which doubles up as the primary key for this 2 column table. Just for resolving many-to-many. So within Entity Framework 6 I have the dal classes.
public partial class User
{
public User()
{
this.Roles = new HashSet<Role>();
}
public int UserId { get; set; }
public int ApplicationId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool IsAnonymous { get; set; }
public System.DateTime LastActivityDate { get; set; }
public virtual Application Application { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public partial class Role
{
public Role()
{
this.Users = new HashSet<User>();
}
public int RoleId { get; set; }
public int ApplicationId { get; set; }
public string RoleName { get; set; }
public string Description { get; set; }
public virtual Application Application { get; set; }
public virtual ICollection<User> Users { get; set; }
}
So I need to get at the fields within my composite table. I wondered if anyone can provide me with assistance on how to resolve this within OnModelCreating in the dbcontext. So that I can then do something with hasKeys
If you only need to change the Id's name or the table's name you can do that with this configuration:
modelBuilder.Entity<User>()
.HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(cs =>
{
cs.MapLeftKey("UserRefId");
cs.MapRightKey("RoleRefId");
cs.ToTable("UsersInRoles");
});
Now if you need to get access explicitly to the junction table or you need to add it new columns, unfortunately you'll need to map it as part of your model.
public partial class User
{
public User()
{
this.Roles = new HashSet<Role>();
}
public int UserId { get; set; }
//...
public virtual ICollection<UsersInRoles> Roles { get; set; }
}
public partial class Role
{
public Role()
{
this.Users = new HashSet<User>();
}
public int RoleId { get; set; }
public virtual ICollection<UsersInRoles> Users { get; set; }
}
public class UsersInRoles
{
[Key, Column(Order = 0),ForeignKey("User")]
public int UserId { get; set; }
[Key, Column(Order = 1),ForeignKey("Role")]
public int RoleId { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
//add new properties here
}
If you prefer use Fluent Api, then remove the data annotations and add these configurations in the OnModelCreating method on your context:
modelBuilder.Entity<UsersInRoles>()
.HasKey(ur=> new { ur.UserId, ur.RoleId});
modelBuilder.Entity<UsersInRoles>()
.HasRequired(ur=> ur.User)
.WithMany(u => u.Roles)
.HasForeignKey(ur=> ur.UserId);
modelBuilder.Entity<UsersInRoles>()
.HasRequired(ur=> ur.Role)
.WithMany(u => u.Users)
.HasForeignKey(ur=> ur.RoleId);
I have 3 classes in my model as you can see below.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
public class MartialArtUserProfile
{
public int UserProfileId { get; set; }
public UserProfile UserProfile { get; set; }
public int MartialArtId { get; set; }
public MartialArt MartialArt { get; set; }
}
And I have a configuration class for many to many relationship as below:
public class MartialArtUserProfileConfiguration : EntityTypeConfiguration<MartialArtUserProfile>
{
public MartialArtUserProfileConfiguration()
{
HasKey(a => new { a.MartialArtId, a.UserProfileId });
HasRequired(a => a.MartialArt)
.WithMany(s => s.MartialArtUserProfiles)
.HasForeignKey(a => a.MartialArtId)
.WillCascadeOnDelete(false);
HasRequired(a => a.UserProfile)
.WithMany(p => p.MartialArtUserProfiles)
.HasForeignKey(a => a.UserProfileId)
.WillCascadeOnDelete(false);
}
}
After defining my entities an relation when I try to run Update-Database in Package Manager Console, it says:
One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'MartialArtUserProfile' has no key defined. Define the key for this EntityType.
\tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'MartialArtUserProfiles' is based on type 'MartialArtUserProfile' that has no keys defined.
What am I doing wrong?
Thanks in advance,
If I understand you are simply trying to create a many to many with a transitive table. If so this is another way to approach this. Use Fluent API to map as below. You can change the UserProfileToMartialArt to whatever you want the table name to be. Instead of creating the MartialArtUserProfile model let EF create the middle ground for you. This also specifies your keys which should get you around the error.
modelBuilder.Entity<UserProfile>()
.HasMany(b => b.MartialArts)
.WithMany(a => a.UserProfiles)
.Map(m => m.MapLeftKey("MartialArtId")
.MapRightKey("UserProfileId")
.ToTable("UserProfileToMartialArt"));
In MartialArts Model put
public IList<UserProfile> UserProfiles { get; set; }
In UserProfile Model put
public IList<MartialArt> MartialArts { get; set; }
Try doing it like this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
[InverseProperty("UserProfiles")]
public IList<MartialArt> MartialArts { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
[InverseProperty("MartialArts")]
public IList<UserProfile> UserProfiles { get; set; }
}
In EntityFramework 6.1, you don't need to do any of this - just add collections of the two types to each class and everything falls into place.
public class UserProfile {
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<MartialArt> MartialArts { get; set; }
public UserProfile() {
MartialArts = new List<MartialArt>();
}
}
public class MartialArt {
public int Id { get; set; }
public string Name { get; set; }
// *snip*
public virtual ICollection<UserProfile> UserProfiles { get; set; }
public MartialArt() {
UserProfiles = new List<UserProfile>();
}
}