EF Core 6: Reading two tables using navigations doesn't load child table data - entity-framework-core

The role property of a relation record doesn't get read/initialized (i.e. the data from the included table doesn't get read). Why is that? The data is in the database.
public class IDM_Role
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long role_id { get; set; } = -1;
[StringLength(80)]
public string role_name { get; set; } = string.Empty;
}
public class IDM_Relation
{
[Required]
[StringLength(80)]
public string account_id { get; set; } = string.Empty;
[Required]
[ForeignKey("IDM_Role")]
public long role_id { get; set; } = 0;
[ForeignKey("role_id")]
public virtual IDM_Role role { get; set; }
}
modelBuilder.Entity<IDM_Relation>()
.HasKey(e => new { e.role_id, e.account_id })
.HasName("PK_IDM_Relation");
[EnableQuery(PageSize = 15)]
public IQueryable<IDM_Relation> Get()
{
return _context.idm_relations.Include(e => e.role);
}
Removing "virtual" from the property IDM_Role role doesn't help.

Related

EF Include and ThenInclude

I have a few models I am trying to bind with Include which is not returning back all expected related data. The full chain is:
User (one) > Role (one) > Permissions (Many) > Entity (One) > EntityArea (One)
These are my models: (CompanyBase is a base class with a companyId in it)
public class User : _CompanyBase
{
public int UserID { get; set; }
public string FullName { get; set; }
public int RoleID { get; set; }
public Role Role { get; set; }
}
public class Role : _CompanyBase
{
[Key, Column(Order = 1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RoleID { get; set; }
[Required]
[StringLength(100, MinimumLength = 3)]
public string Name { get; set; }
public ICollection<RolePermission> RolePermissions { get; set; }
public ICollection<User> Users { get; set; }
}
public class RolePermission : _CompanyBase
{
[Key, Column(Order = 1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RolePermissionID { get; set; }
public Guid Id { get; set; }
[Required]
[StringLength(100, MinimumLength = 3)]
public string PermissionCode { get; set; }
public int RoleID { get; set; }
public Role Role { get; set; }
public int EntityID { get; set; }
public Entity Entity { get; set; }
}
public class Entity : _CompanyBase
{
[Key, Column(Order = 1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EntityID { get; set; }
[Required]
[StringLength(100, MinimumLength = 3)]
public string DisplayName { get; set; }
public int EntityAreaID { get; set; }
public EntityArea EntityArea { get; set; }
}
public class EntityArea :_CompanyBase
{
[Key, Column(Order = 1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EntityAreaID { get; set; }
[Required]
[StringLength(50, MinimumLength = 3)]
public string Name { get; set; }
public ICollection<Entity> Entities { get; set; }
}
And I am trying to bind them with:
dbUser = db.Users
.AsNoTracking()
.Where(x => x.UserId == UserID)
.Include(m => m.Role)
.ThenInclude(m => m.RolePermissions)
.ThenInclude(m => m.Entity)
.ThenInclude(m => m.EntityArea)
.FirstOrDefault();
However, I do get the Role, I'm not getting anything further (Rolepermissions collection, Entity and Area). Is there something fundamentally I am doing wrong? This is a readonly query so hence notracking being used.
Thanks!
I don't think there is anything fundamentally wrong with your attempt. Have you checked if the specific user actually has any data in the connected tables?
Include() can sometimes generate a left join when you don't really need it, so I'd advise you to stay away from it when you can. I'd generally use a projection to specify the data I want to recieve. For your example this can be done with:
dbUser = db.Users
.AsNoTracking()
.Where(x => x.UserId == UserID)
.Select(x => new
{
Role = x.Role,
RolePermissions = x.RolePermissions,
Entity = x.Entity,
EntityArea = x.EntityArea
})
.FirstOrDefault();

How to insert values into a table with optional relationship in ef?

I have those two entities, PatientRegistry and PatientAccount. The relationship between them is 1-0..1. I am trying to seed in my primary table PatientRegistry but I keep getting null errors for required fields in my optional table PatientAccount, How can I insert values in my primary table without the need to seed in my optional table as well?
Unhandled Exception: Microsoft.EntityFrameworkCore.DbUpdateException:
An error occurred while updating the entries. See the inner exception
for details. ---> System.Data.SqlClient.SqlException: Cannot insert
the value NULL into column 'Password', table
'ArtCoreDb.dbo.AspPatientsAccount'; column does not allow nulls.
UPDATE fails.
public class PatientRegistry {
[DatabaseGenerated (DatabaseGeneratedOption.Identity)]
[Display (Name = "Record Id")]
public long RecordId { get; set; }
[Key, DatabaseGenerated (DatabaseGeneratedOption.None)]
[Display (Name = "Patient File Number")]
public long PatientFileId { get; set; }
[Required, StringLength (50)]
[Display (Name = "First Name")]
public string FirstName { get; set; }
public virtual ICollection<PartnerRegistry> Partners { get; set; }
public virtual PatientAccount PatientAccount { get; set; }
}
public class PatientAccount {
[Key, DatabaseGenerated (DatabaseGeneratedOption.Identity)]
public long RecordId { get; set; }
[Required]
public string Password { get; set; }
[Required, StringLength (15)]
public string MobileNo { get; set; }
public bool IsConfirmedPhoneNumber { get; set; } = false;
public bool IsConfirmedEmailAddress { get; set; } = false;
public bool IsLocked { get; set; } = false;
public int? FailedAttempts { get; set; }
public DateTimeOffset? LastLoggedIn { get; set; }
public DateTimeOffset DateCreated { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public long? PatientFileId { get; set; }
public virtual PatientRegistry PatientFile { get; set; }
}
And my fluent API,
protected override void OnModelCreating (ModelBuilder builder) {
base.OnModelCreating (builder);
builder.Entity<PatientRegistry> ()
.HasOne (a => a.PatientAccount)
.WithOne (b => b.PatientFile)
.HasForeignKey<PatientAccount> (c => c.PatientFileId)
.OnDelete (DeleteBehavior.Cascade);
}
And I seed,
if (!context.PatientsRegistry.Any ()) {
context.PatientsRegistry.AddRange (
new PatientRegistry {
PatientFileId = 1111,
FirstName = "John"
}
);
context.SaveChanges ();
}
works only if I add ,,
PatientAccount =
new PatientAccount {
PatientFileId = 1111,
CountryCodeId = context.Countries.Where (g => g.Name == "United States of America").SingleOrDefault ().Id,
AreaCode = 424,
Email = "aamaizar#gmail.com",
MobileNo = "3244990",
IsConfirmedEmailAddress = false,
IsConfirmedPhoneNumber = false,
IsLocked = false,
Password = "213123",
},
in reality, I want to be able to insert in PatientsRegistry table without the need to insert into PatientAccount
Using Add (or AddRange) marks all reachable entities in the graph in added state, I just have to added PatientAccount prop to my object during seeding
new PatientRegistry { PatientFileId = 1111, FirstName = "John", PatientAccount = {.......} }
Ref

update foreign key using entity framework

i am trying to update foreign key in a table named(Friendship).The foreign key is of the table named(FriendshipStatus) the problem is that all the values are updated except the foreign key. I m using code first approach.
Friendship Class
public class Friendship
{
public int Id { get; set; }
public User UserOne { get; set; }
public User UserTwo { get; set; }
public FriendshipStatus Status { get; set; }
public User ReqSB { get; set; }
public RelationType RelationType { get; set; }
public Relationship Relationship { get; set; }
public DateTime FriendshipDate { get; set; }
}
FriendshipStatus class
public class FriendshipStatus
{
public int Id { get; set; }
public string Name { get; set; }
}
Here is the code for update
using (context)
{
Friendship f = getFrienshipRecord(u1, u2); // get single record from db which is to be updated
if (f != null)
{
Friendship ff = new Friendship();
ff.Status = new FriendshipStatus() { Id = 2}; //actually wants to update this this field
ff.Id = f.Id;
ff.FriendshipDate = DateTime.Now;
context.Entry(ff).State = EntityState.Modified;
context.SaveChanges();
}
}
The above code changes datetime but it does not change foreign key.
This is the technique I use for updates that include a child. First, I like to expose the Foreign Key as part of the parent. If you name it FriendshipStatusId, EF will make the association automatically or you can add an annotation or fluent code if preferred:
public class Friendship
{
public int Id { get; set; }
public User UserOne { get; set; }
public User UserTwo { get; set; }
public int? FriendshipStatusId { get; set; } // optional FK
public FriendshipStatus Status { get; set; }
public User ReqSB { get; set; }
public RelationType RelationType { get; set; }
public Relationship Relationship { get; set; }
public DateTime FriendshipDate { get; set; }
}
Now you can do your update by simply fetching the entity (which puts it under tracking) and updating the FK:
using (context)
{
Friendship f = getFrienshipRecord(u1, u2); // get single record from db which is to be updated
if (f != null)
{
f.FriendshipDate = DateTime.Now;
f.FriendshipStatusId = 2;
context.SaveChanges();
}
}
Note that if you add the FK you may need to do a migration or regenerate your database because the EF default might be something like FriendshipStatus_Id.

N to M relationship code first does not create the foreign key on the M-table

A SchoolclassCode can have many Pupils.
A Pupil can belong to many SchoolclassCodes.
This is an N to M relation.
I thought N to M relation work in code first by default.
But I also explicitly create the N to M relation here:
modelBuilder.Entity<SchoolclassCode>().
HasMany(c => c.Pupils).
WithMany(p => p.SchoolclassCodes).
Map(
m =>
{
m.MapLeftKey("SchoolclassCodeId");
m.MapRightKey("PupilId");
m.ToTable("SchoolclassCodePupil");
});
public class SchoolclassCode
{
public SchoolclassCode()
{
Pupils = new HashSet<Pupil>();
Tests = new HashSet<Test>();
}
public int Id { get; set; }
public string SchoolclassCodeName { get; set; }
public string SubjectName { get; set; }
public int Color { get; set; }
public string ClassIdentifier { get; set; }
public ISet<Pupil> Pupils { get; set; }
public ISet<Test> Tests { get; set; }
public Schoolyear Schoolyear { get; set; }
public int SchoolyearId { get; set; }
}
public class Pupil
{
public Pupil()
{
PupilsTests = new HashSet<PupilTest>();
SchoolclassCodes = new HashSet<SchoolclassCode>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Postal { get; set; }
public string City { get; set; }
public string Street { get; set; }
public ISet<PupilTest> PupilsTests { get; set; }
public ISet<SchoolclassCode> SchoolclassCodes { get; set; }
}
On the Pupil Table no foreign key is created at all, Why this?
For a many to many relationship, there is no foreign key on either side. The foreign keys are on the join table, which you have mapped to the table SchoolclassCodePupil:
modelBuilder.Entity<SchoolclassCode>().
HasMany(c => c.Pupils).
WithMany(p => p.SchoolclassCodes).
Map(m =>
{
m.MapLeftKey("SchoolclassCodeId");
m.MapRightKey("PupilId");
m.ToTable("SchoolclassCodePupil");
});
Entity Framework uses that junction table to determine what belongs in the somePupil.SchoolclassCodes set.

Entity Framework Code First Many to Many Setup For Existing Tables

I have the following tables Essence, EssenseSet, and Essense2EssenceSet
Essense2EssenceSet is the linking table that creates the M:M relationship.
I've been unable to get the M:M relationship working though in EF code first though.
Here's my code:
[Table("Essence", Schema = "Com")]
public class Essence
{
public int EssenceID { get; set; }
public string Name { get; set; }
public int EssenceTypeID { get; set; }
public string DescLong { get; set; }
public string DescShort { get; set; }
public virtual ICollection<EssenceSet> EssenceSets { get; set; }
public virtual EssenceType EssenceType { get; set; }
}
[Table("EssenceSet", Schema = "Com")]
public class EssenceSet
{
public int EssenceSetID { get; set; }
public int EssenceMakerID { get; set; }
public string Name { get; set; }
public string DescLong { get; set; }
public string DescShort { get; set; }
public virtual ICollection<Essence> Essences { get; set; }
}
[Table("Essence2EssenceSet", Schema = "Com")]
public class Essence2EssenceSet
{
//(PK / FK)
[Key] [Column(Order = 0)] [ForeignKey("Essence")] public int EssenceID { get; set; }
[Key] [Column(Order = 1)] [ForeignKey("EssenceSet")] public int EssenceSetID { get; set; }
//Navigation
public virtual Essence Essence { get; set; }
public virtual EssenceSet EssenceSet { get; set; }
}
public class EssenceContext : DbContext
{
public DbSet<Essence> Essences { get; set; }
public DbSet<EssenceSet> EssenceSets { get; set; }
public DbSet<Essence2EssenceSet> Essence2EssenceSets { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Essence>()
.HasMany(e => e.EssenceSets)
.WithMany(set => set.Essences)
.Map(mc =>
{
mc.ToTable("Essence2EssenceSet");
mc.MapLeftKey("EssenceID");
mc.MapRightKey("EssenceSetID");
});
}
}
This is the code I'm trying to run:
Essence e = new Essence();
e.EssenceTypeID = (int)(double)dr[1];
e.Name = dr[2].ToString();
e.DescLong = dr[3].ToString();
//Get Essence Set
int setID = (int)(double)dr[0];
var set = ctx.EssenceSets.Find(setID);
e.EssenceSets = new HashSet<EssenceSet>();
e.EssenceSets.Add(set);
ctx.Essences.Add(e);
ctx.SaveChanges();
And here's the error:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception.
I'm not able to find the problem. I'd greatly appreciate help setting this up right.
Thanks!
Remove your Essence2EssenceSet model class. If junction table contains only keys of related entities participating in many-to-many relations it is not needed to map it as entity. Also make sure that your fluent mapping of many-to-many relations specifies schema for table:
mb.Entity<Essence>()
.HasMany(e => e.EssenceSets)
.WithMany(set => set.Essences)
.Map(mc =>
{
mc.ToTable("Essence2EssenceSet", "Com");
mc.MapLeftKey("EssenceID");
mc.MapRightKey("EssenceSetID");
});