Entity Framework Core + MariaDB - Navigation Property is null - mariadb

I have a many-to-many relationship defined using a table in MariaDB. I am trying to use Navigation Properties in Entity Framework Core for the related entities, but they're not hydrated.
roles
| role_id | role_name |
| 1 | Role-A |
| 2 | Role-B |
groups
| group_id | group_name |
| 1 | Group-A |
| 2 | Group-B |
role_to_group
| role_to_group_id | role_id | group_id |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 2 | 2 |
This is the class
[Table(name: "role_to_group")]
public class RoleToGroup
{
[Column(name: "role_to_group_id")]
public long RoleToGroupId { get; set; }
[Column(name: "role_id")]
[ForeignKey("RGToRoles")]
public int RoleId { get; set; }
[Column(name: "group_id")]
public int UserGroupId { get; set; }
[Include]
public Role Role { get; set; }
[Include]
public UserGroup UserGroup { get; set; }
}
The [Include] is a custom defined attribute for hydrating the navigation properties using the pattern similar to DbContext.Set<T>().Include("property_name") via an extension method that does reflection on T and finds all properties with the attribute [Include] defined on them.
However, I am unable to get this to work correctly. The navigation properties Role and UserGroup return null.
What do you suggest I am doing wrong? Also please let me know if you need any more details to be added to the question!!

The problem might be that EF Core doesn't automatically do lazy loading. ( Documentation about how to implement lazy loading ).
When installed these NuGet packages:
Microsoft.EntityFrameworkCore.Proxies
Microsoft.EntityFrameworkCore.Relational
and enabled lazy loading as e.g.:
public class MyDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySQL(#"User Id=root;Host=localhost;Database=Test;");
optionsBuilder
.UseLazyLoadingProxies();
}
public DbSet<UserGroup> UserGroups { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<RoleToGroup> RoleToGroups { get; set; }
}
I got it loaded:
using (var context = new MyDbContext())
{
var roleGroup1 = context.RoleToGroups.First();
Console.WriteLine(roleGroup1.Role.Name); //Role-A
Console.WriteLine(roleGroup1.UserGroup.Name); //Group-A
}
Lazy loading requires types to be public, and properties to be lazy-loaded must be virtual:
[Table(name: "role_to_group")]
public class RoleToGroup
{
[Column(name: "role_to_group_id")]
public int RoleToGroupId { get; set; }
[Column(name: "role_id")]
[ForeignKey("roles")]
public int RoleId { get; set; }
[Column(name: "group_id")]
[ForeignKey("groups")]
public int UserGroupId { get; set; }
public virtual Role Role { get; set; } //must be virtual
public virtual UserGroup UserGroup { get; set; }//must be virtual
}
[Table(name: "roles")]
public class Role
{
[Key]
[Column(name: "role_id")]
public int RoleId { get; set; }
[Column(name: "role_name")]
public string Name { get; set; }
}
[Table(name: "groups")]
public class UserGroup
{
[Key]
[Column(name: "group_id")]
public int GroupId { get; set; }
[Column(name: "group_name")]
public string Name { get; set; }
}
Alternatively to lazy loading, manual Eager or Explicit loading could be used from the same doc.

Related

ef core shared child table with one-to-many

I would like to have a shared table that can be somehow referenced to multiple parent tables. The parent tables can have multiple rows from the shared Attachement table. The Type/TypeId would be a column which points to the parent table (like if the parentId is in Person or Company table)
Shared table:
Id | ParentId | Type/TypeId | Value
1 1 Person/1 "somestringvalue"
2 1 Person/1 "another value"
3 3 Company/2 "value"
....
The models would look something like this
public class Attachement
{
[Key]
public int Id { get; set; }
//PK- Id of the parent table
public int ParentId { get; set; }
// type or enum of a parent table. Should point to which table it points to
public int Type { get; set; }
public string Value { get; set; }
}
public class Person
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
//shared table
public ICollection<Attachement> Attachements { get; set; }
}
public class Company
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
//shared table
public ICollection<Attachement> Attachements { get; set; }
}
Sidenote also - im using code first pattern.
Thanks!

Not generating unique GUID

I got a very weird event when storing data.
My table uses Guid as primary ID, and for some reason it is recording the same Guid for all new entries.
Table constructs
public class Checkpoint
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public string Title { get; set; }
public DateTime? CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
}
public class Track
{
public Track()
{
Checkpoints = new List<Checkpoint>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public List<Checkpoint> Checkpoints { get; set; }
public DateTime? CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
}
Code snippet that converts from model to list of new Checkpoints stored in track.
var trackObject = new Track();
var checkpointList = model.Checkpoints.ConvertAll(x => new Checkpoint {Title = x.Title});
trackObject.Checkpoints.Add(checkpointList);
db.Track.Add(trackObject);
await db.SaveChangesAsync();
Checkpoint table result (i omited the foreign key pointing to Track object):
ID | TITLE | CREATED_ON
--------------------------------------------------------------------------------
c3451b2b-bb30-e711-b867-f01faf23929d | First | 4/05/2017 11:16:50 AM
c5451b2b-bb30-e711-b867-f01faf23929d | Second | 4/05/2017 11:16:50 AM
c6451b2b-bb30-e711-b867-f01faf23929d | Third | 4/05/2017 11:16:50 AM
c7451b2b-bb30-e711-b867-f01faf23929d | Sprint | 4/05/2017 11:16:50 AM
c8451b2b-bb30-e711-b867-f01faf23929d | Home | 4/05/2017 11:16:50 AM
c9451b2b-bb30-e711-b867-f01faf23929d | Finish | 4/05/2017 11:16:50 AM
As can be seen all the ID fields are the same.
Where is the fault in the code?
These are all unique and different. Look at the second digit of these posted guids.
The EF code tells SQL to generate new Sequential Guids (TSQL NEWSEQUENTIALID()).
https://learn.microsoft.com/en-us/sql/t-sql/functions/newsequentialid-transact-sql

Many-to-many relations in Entity Framework

I have an issue with many-to-many relations.
I have 3 model classes:
Article - >>> Item
Keyword - >>> Keyword
TableForRelation between Articles And Keywords - >>> ItemKeywords
With Entity Framework Core, I write these 3 classes and they work fine
public class Item
{
public int Id { get; set; }
public string Content { get; set; }
public virtual ICollection<ItemKeyWords> ItemKeyWords { get; set; }
}
public class KeyWord
{
public int Id { get; set }
public string Text { get; set; }
public virtual ICollection<ItemKeyWords> ItemKeyWords { get; set; }
}
public class ItemKeyWords
{
public int Id { get; set; }
public int ItemId { get; set; }
public virtual Item Item { get; set; }
public int KeyWordId { get; set; }
public virtual KeyWord KeyWord { get; set; }
}
Question is: how can I tell Entity Framework if Keyword exists do not put that in keyword table and just create a relation to that in ItemKeywords table.
database uml
before to add a KeyWord to Item.ItemKeyWords you have to try to load it from context.Set<KeyWord>().
If the load results on a null, then do as actually.
If the load != null then add the loaded value.

EF Code First, specify relationship table?

Classes:
public class Action
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<User> Users{ get; set; }
}
public class User
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<Action> Actions{ get; set; }
}
Tables:
Actions
| ID | Name |
Users
|ID | Name |
ActionUsers
| Action_ID | User_ID |
Note that Action and User has many-to-many relationship.
Assuming that EF can map the above example successfully (I simplified my original code), then I decided to do the following:
rename the table ActionUsers to Permissions
rename member of Action class, Users to PermittedUsers
rename member of User class, Actions to Permissions
How would I tell the EF to use Permissions as relationship table instead of ActionUsers when mapping User.Permissions and Action.PermittedUsers? Can I achieve the desired configuration without using Fluent API?
Using Fluent API :
public class Action
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<User> PermittedUsers{ get; set; }
}
public class User
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<Action> Permissions{ get; set; }
}
you can simply override OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Action>().
HasMany(c => c.PermittedUsers).
WithMany(p => p.Permissions).
Map(
m =>
{
m.MapLeftKey("Action_ID");
m.MapRightKey("User_ID");
m.ToTable("Permissions");
});
}
if you want to have your own naming convention you can define Permission Table as a class :
public class Permissions
{
[ForeignKey("Action")]
public int Action_ID { get; set; }
public Action Action { get; set; }
[ForeignKey("User")]
public int User_ID { get; set; }
public User User { get; set; }
}
// context
public class MyContext : DbContext
{
public DbSet<Action> Actions { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Permissions> Permissions { get; set; }
}

Advice on structuring access control in database using entity framework

I am looking for some advice and assistance with an architecture I am attempting to implement. The application I am building provides SSO and central authentication for multiple ASP.NET websites that we use internally. The problem I am facing is that I cannot seem to come up with a way to structure my data in entity framework that makes sense.
Overview:
1) An account can be associated with many applications
2) Roles are specific to each application
3) Features are specific to each application
4) It is not shown below but I would like to further break down the associations and make Accounts members of “Directories” and an application can access users within a list of directories associated with the application (does this make sense?)
A user “Joe” would have permissions like below:
Application 1:
Role | Feature 1 | Feature 2 | Feature 3
=============================================
Role1 | x | | x
Role2 | | x | x
Role3 | x | x | x
Application 2:
Role | Feature 1 | Feature 2 | Feature 3
=============================================
Role1 | | | x
Role2 | | x |
Role3 | x | x | x
I would access these permissions via an API similar to:
CheckAccountPermission(Role, Feature), IsAccountInRole(Role)
Currently, I have been trying the following model:
Application.cs
public int ApplicationId { get; set; }
public Guid ApplicationGuid { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsEnabled { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? LastModified { get; set; }
public virtual ICollection<Directory> Directories { get; set; }
public virtual ICollection<Feature> Features { get; set; }
public virtual ICollection<AccountPermission> AccountPermissions { get; set; }
Account.cs
public int AccountId { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string PasswordSalt { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string AvatarUrl { get; set; }
public string AccountNotes { get; set; }
public DateTime? LastLogin { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? LastModified { get; set; }
public DateTime? LastActivity { get; set; }
public bool IsEnabled { get; set; }
//-------------------//
public int DirectoryId { get; set; }
public virtual Directory Directory { get; set; }
public virtual ICollection<Session> Sessions { get; set; }
public virtual ICollection<AccountPermission> AccountPermissions { get; set; }
Directory.cs
public string Name { get; set; }
public string Description { get; set; }
public bool IsEnabled { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
public virtual ICollection<Role> Roles { get; set; }
public virtual ICollection<Application> Applications { get; set; }
Role.cs
public int RoleId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? PrimaryRoleId { get; set; }
public Role PrimaryRole { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? LastModified { get; set; }
public virtual int DirectoryId { get; set; }
public virtual Directory Directory { get; set; }
public virtual ICollection<AccountPermission> AccountPermissions { get; set; }
AccountPermission.cs
[Key, Column(Order = 0)]
public int AccountId { get; set; }
public virtual Account Account { get; set; }
[Key, Column(Order = 1)]
public int RoleId { get; set; }
public virtual Role Role { get; set; }
[Key, Column(Order = 2)]
public int FeatureId { get; set; }
public virtual Feature Feature { get; set; }
[Key, Column(Order = 3)]
public int ApplicationId { get; set; }
public virtual Application Application { get; set; }
If I am off base or misguided here, please feel free to point me in the right direction. I really want to get this right. Any clarifying information I can provide, please let me know!
Thanks everyone!

Resources