How can I create ManyToMany relationship between AspNetRoles and Menus table - many-to-many

I am using asp.net mvc identity. I want to create ManyToMany relationship between Roles and Menu table
I found this to create a ManyToMany relationship in EntityFramework between two tables
on
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
public class Role
{
public Role() { }
public int RoleId { get; set; }
public string RoleName { get; set; }
public virtual ICollection<Menu> Menus { get; set; }
}
public class Menu
{
public Menu()
{
this.Roles = new HashSet<Role>();
}
public int MenuId { get; set; }
public string MenuName { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
I am unable to find AspNetRoles Class. My question is how can i create relationship between existing identity tables. ManyToMany, OneToMany and ManyToOne

After some search Here I found my answer
How to implement a relationship between IdentityRole (dbo.AspNetRoles) and a custom entity?
We only have to define this property
public virtual IdentityRole IdentityRole { get; set; }
in role dependent class which will solve the problem.
The reason for not deleting this post is to cover all possible aspects of the questions for this answer

I was trying to follow your solution, based of course on the problem you proposed.
I edited the first code you posted in order to get it working properly. I want to explain the use of the Identities Tables and one proper way to extend or customize them.
Note:
This code will work when using ManyToOne or OneToOne relationships
public virtual IdentityRole IdentityRole { get; set; }
But I wouldn't recommend it, because you will get some weird and unexpected result at some point of your Model Relationships design.
Now I have to say, this was the way it worked for me, I tested a lot and always got what I wanted.
The trick is easy, Always extend your IdentityModel, doesn't matter which one... and then do everything to this model that extended the Original Identity (relationship, referencing, adding properties, ect...).
With all that said, Let's see the sample now:
To get the ManyToMany relationship that you where trying to get, you must extend the IdentityRole class, and then work with it for customization.
I will focus on the ManyToMany relationship example, because I have the feeling is the less common sample out there.
So here we go :
DBContext is the same for both samples:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
.
.
.
override
public IDbSet<IdentityRole> Roles { get; set; }
public DbSet<Menu> Menus { get; set; }
}
Entities:
public class Role : IdentityRole
{
public Role() { }
public string RoleName { get; set; }
public virtual ICollection<Menu> Menus { get; set; }
}
public class Menu
{
public Menu()
{
this.Roles = new HashSet<Role>();
}
public int Id{ get; set; }
public string MenuName { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
Which generates this migration:
CreateTable(
"dbo.Menus",
c => new
{
Id = c.Int(nullable: false, identity: true),
MenuName = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.AspNetRoles",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Name = c.String(nullable: false, maxLength: 256),
RoleName = c.String(),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.Index(t => t.Name, unique: true, name: "RoleNameIndex");
CreateTable(
"dbo.RoleMenus",
c => new
{
Role_Id = c.String(nullable: false, maxLength: 128),
Menu_Id = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.Role_Id, t.Menu_Id })
.ForeignKey("dbo.AspNetRoles", t => t.Role_Id, cascadeDelete: true)
.ForeignKey("dbo.Menus", t => t.Menu_Id, cascadeDelete: true)
.Index(t => t.Role_Id)
.Index(t => t.Menu_Id);
As you can see, there is a pivot table that was automatically created RoleMenus and automatically added the respective relationships.
On the other side, if someone follow your example and use the IdentityRole to create a ManyToMany relationship, using the ICollection .
Let's see :
public class Role : IdentityRole
{
public Role() { }
public string RoleName { get; set; }
public virtual ICollection<Menu> Menus { get; set; }
}
public class Menu
{
public Menu()
{
this.Roles = new HashSet<IdentityRole>();
}
public int Id{ get; set; }
public string MenuName { get; set; }
public virtual ICollection<IdentityRole> Roles { get; set; }
}
Generating :
CreateTable(
"dbo.Menus",
c => new
{
Id = c.Int(nullable: false, identity: true),
MenuName = c.String(),
Role_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AspNetRoles", t => t.Role_Id)
.Index(t => t.Role_Id);
CreateTable(
"dbo.AspNetRoles",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Name = c.String(nullable: false, maxLength: 256),
RoleName = c.String(),
Discriminator = c.String(nullable: false, maxLength: 128),
Menu_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Menus", t => t.Menu_Id)
.Index(t => t.Name, unique: true, name: "RoleNameIndex")
.Index(t => t.Menu_Id);
As you can see, the relationship created was a trial of a OneToOne, not the result we were expecting and if we reference the original IdentityModel to do some sort of relationship, we'll always gonna get it wrong at some point.
So I guess this was it, sorry for the long post, but I felt that somebody should have talked about how to edit the IdentityModel or at least in one functional way.
So I hope this helps somebody that run into same necessity in the future.
Sorry for my English.
Regards.

Related

ASP.Net Core - EntityFrameworkCore data is not adding, updating instead

I am using ASP.Net Core 2 and Entity Framework Core with MySQL.
I want to add a simple entity to the database.
My Model is like this-
public class Employee
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string City { get; set; }
[Required]
public string Department { get; set; }
[Required]
public int Salary { get; set; }
}
I have configured fluent API in DBContext like this-
//Key automatic generation configuration
modelBuilder.Entity<Employee>()
.Property(b => b.Id)
.ValueGeneratedOnAdd();
And then calling from controller to add content like this-
Employee employee = new Employee
{
City = newString,
Department = newString,
Name = newString,
Salary = DateTime.UtcNow.Millisecond
};
_context.Employee.Add(employee);
_context.SaveChanges();
What I am seeing is updating the first data having ID = 1 and never add new data. I want my ID to be auto-incrementing and don't want to use GUID for maintaining ID. What do I need to do to make it work?
Update-
I have already tried-
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
And
[Key]
public int Id { get; set; }
And
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
But no luck.
The complete code is in this Github Repository.
And the controller is here.
Use the annotation "DatabaseGeneratedOption.Identity" for auto-increment.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
Migration File should be generated as below:
CreateTable(
"dbo.table",
c => new
{
Id = c.Int(nullable: false, identity: true),
.............
})
.PrimaryKey(t => t.Id);
you are mixing data annotations and fluent api configurations which is not a good practice. anyway, entity framework considers int Id in persistence models as key in general so you don't have to specify it manually. I recommend you choosing only one approach (from my experience fluent api is usually better - more extensible for future and persistence models look much cleaner) and try to go without specifying that the Id should or should not change because without it you will get what you want.
your migration should look like this:
migrationBuilder.CreateTable(
name: "TableName",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn)
},
constraints: table =>
{
table.PrimaryKey("PK_TableName", x => x.Id);
});
...

entity framework core with mysql unknown column in field list

I have a users table and a products table
and a many-to-many relationship between them in table UsersProducts.
Each user can request to get more products, And I decided I need to save the date when I gave the user the products
public class UserProduct
{
public int UserId { get; set; }
public User User { get; set; }
public Product Product { get; set; }
public int ProductId { get; set; }
public DateTime DateAssigned { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserProduct>()
.HasKey(bc => new { UserId = bc.UserId, ProductId = bc.ProductId });
modelBuilder.Entity<UserProduct>()
.HasOne(bc => bc.User)
.WithMany(b => b.Products)
.HasForeignKey(bc => bc.UserId);
modelBuilder.Entity<UserProduct>()
.HasOne(bc => bc.Product)
.WithMany(c => c.Owners)
.HasForeignKey(bc => bc.ProductId);
base.OnModelCreating(modelBuilder);
}
When I try to access (UsersProducts.Where(...)) it I get "unknown column in field list" in regards to DateAssigned column.
This is the code EF generated in the migration
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "DateAssigned",
table: "UsersProducts",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DateAssigned",
table: "UsersProducts");
}
It seems the column exists in the table, So why is this happening?
Try to add it in mapping
.Property(t => t.DateAssigned).HasColumnName("DateAssigned")

nPoco V3 - many to many not working

I have users which have many roles
public class User
{
public int Id {get;set;}
public string Name {get;set;}
public List<Role> Roles {get;set;}
}
public class Roles
{
public int Id {get;set;}
public string Key{get;set;}
}
public class UserRoles
{
public int UserId {get;set;}
public int RoleId {get;set;}
}
what I try to achieve is getting a user with all its roles in one query, but so far I failed.
For Mapping I use a custom Conventionbased mapper (I can provide the code, but it's rather big)
I tried FetchOneToMany and I tried Fetch as described here
https://github.com/schotime/NPoco/wiki/One-to-Many-Query-Helpers
https://github.com/schotime/NPoco/wiki/Version-3
But Roles is always empty.
Role and User by itself are mapped correctly and I did try to specify the relation like
For<User>().Columns(x =>
{
x.Many(c => c.Roles);
x.Column(c => c.Roles).ComplexMapping();
}, true);
Again it didn't help, roles is empty.
I have no idea what I'm missing.
Any ideas?
ComplexMapping and relationship mapping(1-to-n, n-to-n) are two different things.
ComplexMapping is for mapping nested objects for data that usually resides in the same table where there is a one-to-one relationship. For something like this:
public class Client
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public Client()
{
Address = new Address();
}
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string Telephone { get; set; }
public string Country{ get; set; }
}
If you're using a convention-based mapper your override would look something like this:
For<Client>().Columns(x =>
{
x.Column(y => y.Address).ComplexMapping();
});
One thing to watch for when using a convention-based mapper; you have to enable ComplexMapping in your scanner with the following code:
scanner.Columns.ComplexPropertiesWhere(y => ColumnInfo.FromMemberInfo(y).ComplexMapping);
Otherwise ComplexMapping() calls in your overrides will simply be ignored.
One-to-Many mapping would work like this (see NPoco on Github for more):
For<One>()
.TableName("Ones")
.PrimaryKey(x => x.OneId)
.Columns(x =>
{
x.Column(y => y.OneId);
x.Column(y => y.Name);
x.Many(y => y.Items).WithName("OneId").Reference(y => y.OneId);
}, true);
For<Many>()
.TableName("Manys")
.PrimaryKey(x => x.ManyId)
.Columns(x =>
{
x.Column(y => y.ManyId);
x.Column(y => y.Value);
x.Column(y => y.Currency);
x.Column(y => y.OneId);
x.Column(y => y.One).WithName("OneId").Reference(y => y.OneId, ReferenceType.OneToOne);
}, true);

Issue with Many-To-Many relationships in MySql

I am using EF Core with MySQL using SapientGuardian's EFCore library and I am trying to create a table with many-to-many relationship using the following code;
public class Personnel
{
public int Id { get; set; }
[MaxLength(100)]
public string Name { get; set; }
public virtual ICollection<PersonnelDegree> PersonnelDegrees { get; set; }
}
public class Degree
{
public int Id { get; set; }
[MaxLength(100)]
public string Name { get; set; }
public virtual ICollection<PersonnelDegree> PersonnelDegrees { get; set; }
}
public class PersonnelDegree
{
public int PersonnelId { get; set; }
public int DegreeId { get; set; }
public virtual Personnel Personnel { get; set; }
public virtual Degree Degree { get; set; }
}
// Inside the OnModelCreating override
builder.Entity<Degree>().HasMany(x => x.Personnel);
builder.Entity<Personnel>().HasMany(x => x.Degrees);
builder.Entity<PersonnelDegree>()
.HasKey(x => new { x.DegreeId, x.PersonnelId });
builder.Entity<PersonnelDegree>()
.HasOne(x => x.Degree)
.WithMany(x => x.PersonnelDegrees)
.HasForeignKey(x => x.DegreeId);
builder.Entity<PersonnelDegree>()
.HasOne(x => x.Personnel)
.WithMany(x => x.PersonnelDegrees)
.HasForeignKey(x => x.PersonnelId);
Now, when I run dotnet ef migration add personnel; I get this...
migrationBuilder.CreateTable(
name: "Role",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("MySQL:AutoIncrement", true),
ApplicationId = table.Column<int>(nullable: true),
Name = table.Column<string>(maxLength: 100, nullable: true),
PersonnelId = table.Column<int>(nullable: true) // Where is this coming from?
},
constraints: table =>
{
table.PrimaryKey("PK_Role", x => x.Id);
table.ForeignKey(
name: "FK_Role_Application_ApplicationId",
column: x => x.ApplicationId,
principalTable: "Application",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
Notice that the table definition has for Role has PersonnelId column in it. And Personnel table has RoleId? Can anybody tell me what is going on here?
I have the same models, but different OnModelCreating method.
So I assume that you configured something wrong. It should be something like this for your case:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PersonnelDegree>()
.HasKey(t => new { t.PersonnelId , t.DegreeId });
}
That is all you should have in your OnModelCreating method.

Entity framework 4.1 relation to table itselft

is there anyone who could tell me how can i do the corresponding table mapping with entity framework code first.
Here is my table
enter link description here
i've tried to do this but without any success.
[Table("Matiere")]
public class Matiere
{
[Key]
public Int32 Id { get; set; }
public Int32? IdParent { get; set; }
[Column("NomMatiere")]
public String Nom { get; set; }
public virtual Matiere Parent { get; set; }
public virtual ICollection<Matiere> Childs { get; set; }
}
public class MatiereConfiguration : EntityTypeConfiguration<Matiere>
{
public MatiereConfiguration()
{
this.HasOptional(m => m.Parent).WithMany(m => m.Childs).HasForeignKey(m => m.IdParent);
this.HasOptional(m => m.Childs).WithRequired();
}
}
thanks in advance.
You're close. I do not think you need to supply the HasOptional(m => m.Childs).WithRequired();
First, I would put all of your mapping information into your MatiereConfiguration instead of using a mix of DataAnnotations and Fluent mappings. It's not required, but just a suggestion.
This should work:
public class MatiereConfiguration : EntityTypeConfiguration<Matiere>
{
public MatiereConfiguration()
{
HasKey(m => m.Id);
Property(m => m.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(m => m.Nom).HasColumnName("NomMatiere")
HasOptional(m => m.Parent).WithMany(m => m.Childs).HasForeignKey(m => m.IdParent);
}
}