Issue with Many-To-Many relationships in MySql - 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.

Related

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);

Automapper Projection with Linq OrderBy child property error

I am having an issue using an AutoMapper (version 5.1.1) projection combined with a Linq OrderBy Child property expression. I am using Entity Framework Core (version 1.0.0). I am getting the following error:
"must be reducible node"
My DTO objects are as follows
public class OrganizationViewModel
{
public virtual int Id { get; set; }
[Display(Name = "Organization Name")]
public virtual string Name { get; set; }
public virtual bool Active { get; set; }
public virtual int OrganizationGroupId { get; set; }
public virtual string OrganizationGroupName { get; set; }
public virtual int StrategyId { get; set; }
public virtual string StrategyName { get; set; }
public virtual OrganizationGroupViewModel OrganizationGroup { get; set; }
}
public class OrganizationGroupViewModel
{
public virtual int Id { get; set; }
[Display(Name = "Organization Group Name")]
public virtual string Name { get; set; }
public virtual bool Active { get; set; }
}
My corresponding entity models are as follows:
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public string TimeZone { get; set; }
public bool Active { get; set; }
//FKs
public int OrganizationGroupId { get; set; }
public int StrategyId { get; set; }
//Navigation
public virtual OrganizationGroup OrganizationGroup { get; set; }
public virtual Strategy Strategy { get; set; }
[JsonIgnore]
public virtual List<AppointmentReminder> AppointmentReminders { get; set; }
}
public class OrganizationGroup
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public virtual List<Organization> Organizations { get; set; }
}
My AutoMapper profiles are as follows:
public class OrganizationMapperProfile : Profile
{
public OrganizationMapperProfile()
{
CreateMap<Task<Organization>, Task<OrganizationViewModel>>();
CreateMap<Organization, OrganizationViewModel>()
.ForMember(dest => dest.OrganizationGroupName, opt => opt.MapFrom(src => src.OrganizationGroup.Name));
CreateMap<OrganizationInput, Organization>()
.ForMember(x => x.Id, opt => opt.Ignore());
}
}
public class OrganizationGroupMapperProfile : Profile
{
public OrganizationGroupMapperProfile()
{
CreateMap<Task<OrganizationGroup>, Task<OrganizationGroupViewModel>>();
CreateMap<OrganizationGroup, OrganizationGroupViewModel>();
CreateMap<OrganizationGroupInput, OrganizationGroup>()
.ForMember(x => x.Id, opt => opt.Ignore());
}
}
When I run the following statements I am able to run and get results from the first 2 statements:
var tmp = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.OrganizationGroup.Name).ToListAsync();
var tmp4 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("OrganizationGroup.Name").ToListAsync();
But when I add the ProjectTo I get the error listed above:
var tmp5 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.OrganizationGroup.Name).ProjectTo<OrganizationViewModel>().ToListAsync();
var tmp6 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("OrganizationGroup.Name").ProjectTo<OrganizationViewModel>().ToListAsync();
As some additional information, I am able to OrderBy with Projections working on properties of the parent class, such as:
var tmp7 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.Name).ProjectTo<OrganizationViewModel>().ToListAsync();
var tmp8 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("Name").ProjectTo<OrganizationViewModel>().ToListAsync();
Anyone run into this issue before? Looks like I'm trying to do something that is otherwise not supported, is that by design? Thanks for any help/insight.
Looks like the problem is caused by the OrganizationGroup property of the OrganizationViewModel class - AutoMapper generates a null check which EF Core doesn't like in the combination with your OrderBy (I guess just one of the many bugs currently in EF Core). It can easily be reproduced by the following simple manual projection query:
var tmp5a = _context.Organizations
.OrderBy(x => x.OrganizationGroup.Name)
.Select(e => new OrganizationViewModel
{
Id = e.Id,
OrganizationGroup = e.OrganizationGroup != null ? new OrganizationGroupViewModel
{
Id = e.OrganizationGroup.Id,
Name = e.OrganizationGroup.Name,
Active = e.OrganizationGroup.Active,
} : null,
})
.ToList();
To fix the issue, configure AutoMapper to not generate null check for that property as follows:
CreateMap<Organization, OrganizationViewModel>()
.ForMember(dest => dest.OrganizationGroup, opt => opt.AllowNull())
.ForMember(dest => dest.OrganizationGroupName, opt => opt.MapFrom(src => src.OrganizationGroup.Name));

How can I create ManyToMany relationship between AspNetRoles and Menus table

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.

Entity Framewrok 5 fluent mapping for many to many with 3 entities

I have the following scenario that is giving me a hard time:
public class SegUser
{
public string IdUser { get; set; }
public List<SegRol> SegRoles { get; set; }
public virtual List<SegApps> Apps{ get; set; }
}
public class SegRole
{
public virtual int IdRole { get; set; }
public virtual List<SegUer> SegUsers { get; set; }
public virtual List<SegApp> Apps { get; set; }
}
public class SegApp
{
public virtual int IdApp{ get; set; }
public virtual List<SegUser> Users { get; set; }
public virtual List<SegRole> Roles { get; set; }
}
In my database I have those 3 tables and an extra table with 3 PKs (one for each entity) to establish the relationship between those 3 entities.
How can I achieve the mapping with Entity Framework 5 fluent API.
I've already tried:
private void MapSegUser()
{
//mapping of another fields
entityConfiguration
.HasMany(x => x.SegRoles)
.WithMany(x => x.SegUsers)
.Map(mc =>
{
mc.MapLeftKey("id_role");
mc.MapRightKey("id_user");
mc.ToTable("seg_users_roles");
});
entityConfiguration
.HasMany(x => x.Apps)
.WithMany(x => x.Users)
.Map(mc =>
{
mc.MapLeftKey("id_app");
mc.MapRightKey("id_user");
mc.ToTable("seg_users_roles_apps");
});
}
private void MapSegApp()
{
//mapping of another fields
entityConfiguration
.HasMany(x => x.Users)
.WithMany(x => x.Apps)
.Map(mc =>
{
mc.MapLeftKey("id_user");
mc.MapRightKey("id_app");
mc.ToTable("seg_users_roles_apps");
});
}
private void MapSegRole()
{
//mapping of another fields
entityConfiguration
.HasMany(x => x.SegUsers)
.WithMany(x => x.SegRoles)
.Map(mc =>
{
mc.MapLeftKey("id_user");
mc.MapRightKey("id_role");
mc.ToTable("seg_users_roles");
});
}