I am using IEntityTypeConfigurations
and FluentMigrator migrations for my data access setup. Generally I thought I had a basic understanding of configuring entities and tables for relations to work out and at some point it did work.
However now my UserAccounts
UserRole
is always null
when querying my desired account from the database. Same happens with the permissions for my roles.
Before the current snippet I have had the accounts that utilize a role as a list and I established a relationship between those, but that did not work either. I am a little bit confused and at this point I am just trying stuff out without any understanding, to be honest.
Storing the entities works perfectly, just as I want, as far as I can tell. The entities are in exactly the state I want them to be in, after changes in the context are saved to the database. Meaning all permissions are present in my role and the role is present in my account, with all the data I want.
public class UserAccount
{
public int Id { get; set; }
public string Email { get; set; } = string.Empty;
public string PhoneNumber { get; set; } = string.Empty;
public string LoginName { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public DateTime PasswordChanged { get; set; } = DateTime.Now;
public int RoleId? { get; set; }
public virtual UserRole? Role { get; set; }
}
public class UserAccountEntityMap : IEntityTypeConfiguration<UserAccount>
{
public void Configure(EntityTypeBuilder<UserAccount> builder)
{
builder.ToTable("UserAccounts");
builder.HasKey(x => x.Id);
builder.Property(u => u.Id).HasColumnName("Id").UseIdentityColumn();
builder.Property(u => u.Email).HasColumnName("Email");
builder.Property(u => u.PhoneNumber).HasColumnName("PhoneNumber");
builder.Property(u => u.LoginName).HasColumnName("LoginName");
builder.Property(u => u.PasswordHash).HasColumnName("PasswordHash");
builder.Property(u => u.PasswordChanged).HasColumnName("PasswordChanged");
builder.HasOne(u => u.Role)
.WithMany()
.HasForeignKey(u => u.RoleId);
}
}
// Migration creating the table
public override void Up()
{
if (!Schema.Table("UserAccounts").Exists())
{
Create.Table("UserAccounts")
.WithColumn("Id")
.AsInt32()
.NotNullable()
.PrimaryKey()
.Identity()
.WithColumn("Email")
.AsString()
.WithColumn("PhoneNumber")
.AsString()
.WithColumn("LoginName")
.AsString()
.WithColumn("PasswordHash")
.AsString()
.WithColumn("PasswordChanged")
.AsDateTime2()
.WithColumn("RoleId")
.AsInt32()
.ForeignKey();
Create.ForeignKey("FK_UserAccount_UserRole")
.FromTable("UserAccounts")
.ForeignColumn("RoleId")
.ToTable("UserRoles")
.PrimaryColumn("Id");
}
}
public class UserRole
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public DateTime LastChanged { get; set; } = DateTime.Now;
public virtual List<UserRolePermission> Permissions { get; set; } = new();
}
public class UserRoleEntityMap : IEntityTypeConfiguration<UserRole>
{
public void Configure(EntityTypeBuilder<UserRole> builder)
{
builder.ToTable("UserRoles");
builder.HasKey(t => t.Id);
builder.Property(r => r.Id).HasColumnName("Id").UseIdentityColumn();
builder.Property(r => r.Name).HasColumnName("Name");
builder.Property(r => r.Description).HasColumnName("Description");
builder.Property(r => r.LastChanged).HasColumnName("LastChanged");
builder.HasMany(r => r.Permissions)
.WithOne()
.HasForeignKey(p => p.RoleId);
}
}
// Migration creating the table
public override void Up()
{
if (!Schema.Table("UserRoles").Exists())
{
Create.Table("UserRoles")
.WithColumn("Id")
.AsInt32()
.NotNullable()
.PrimaryKey()
.Identity()
.WithColumn("Name")
.AsString()
.WithColumn("Description")
.AsString()
.WithColumn("LastChanged")
.AsDateTime2();
}
}
I have tried a bunch of stuff and even referenced the configuration we use at work, where I am doing my apprenticeship - my setup is almost mirroring it - but I just can't get it to work.
I tried to make my related-to properties virtual without success, I changed the migrations and entity configurations a bunch of times on new databases. A lot of resources online only reference and present automatically generated model configurations and migrations and even those that don't did not seem to bring me any further than this.
So I have a few concrete questions
Foreign keys are pretty much 'references' describing what table and what column the related-to database object is. Multiple columns can form a foreign key. Am I right about this or am I misinterpreting the information I gathered so far?
Primary keys are pretty much identifyers, obviously unique and can be made up of multiple columns that are required to uniquely describe an entry into the database. Am I right?
It is possible to set up an entity in an entity configuration without a key, what are the use-cases?
And obviously: What is my issue in my attempts above? What did I not understand and what to I need to adjust?
Are you doing lazy loading? Try this
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString);
https://learn.microsoft.com/en-us/ef/core/querying/related-data/lazy