I have an existing database. At the moment I am trying to map my new Entity objects to that DB with entity framework code first. Below is the User class which has a friends collection. As you can see this is a many-to-many relationship to the same table. How can I map this relation to table "user_friend" which has columns "user_id" and "friend_id".
public class User
{
private ICollection<User> _friends = new List<User>();
public ICollection<User> Friends { get{return _firends;} }
}
moduleBuilder.Entity<User>().HasMany????.ToTable("user_friend");
You need to drop down to fluent API for this:
public class User
{
public int UserId { get; set; }
public ICollection<User> Friends { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Friends).WithMany().Map(c =>
{
c.MapLeftKey(u=>u.UserID, "user_id");
c.MapRightKey(f=>f.FriendID, "friend_id");
c.ToTable("user_friend");
});
}
}
Related
Im using mvc5 with codefirst and the user account stuff.
I have a simple requirement a user can belong to multiple stores and a business can have multiple stores.
So far I have the business - many stores working but I cant seem to figure out how to setup the users.
My StoreModel
public virtual Business Business { get; set; }
public virtual ICollection<ApplicationUser> Employees { get; set; }
My BusinessModel
public virtual ICollection<StoreModel> Stores { get; set; }
Within my context I have tried
public class ApplicationUser : IdentityUser
{
//a user can belong to multiple stores
public virtual ICollection<StoreModel> Stores { get; set; }
}
however when I try add-migration the code generated is going to change my table names and does create the join table between Store & AspNetUser
My migration looks like
public override void Up()
{
RenameTable(name: "dbo.Stores", newName: "ApplicationUserStoreModels");
DropForeignKey("dbo.Stores", "ApplicationUser_Id", "dbo.AspNetUsers");
DropIndex("dbo.Stores", new[] { "ApplicationUser_Id" });
CreateIndex("dbo.ApplicationUserStoreModels", "ApplicationUser_Id");
CreateIndex("dbo.ApplicationUserStoreModels", "StoreModel_StoreId");
AddForeignKey("dbo.ApplicationUserStoreModels", "ApplicationUser_Id", "dbo.AspNetUsers", "Id", cascadeDelete: true);
AddForeignKey("dbo.ApplicationUserStoreModels", "StoreModel_StoreId", "dbo.Stores", "StoreId", cascadeDelete: true);
DropColumn("dbo.Stores", "ApplicationUser_Id");
}
Can anyone help what I need to do to get this working?
you can either just not have a store table and put a list in each appuser and business and EF will make your many to many relationship or you can use fluent api to map everything. Should be something like the following.
public class Business
{
public virtual ICollection<StoreModel> Stores { get; set; }
}
public class ApplicationUser : IdentityUser
{
public virtual ICollection<StoreModel> Stores { get; set; }
}
public class StoreModel {
public string ApplicationUserId { get; set; }
public int BusinessId { get; set; }
public virtual Business Businesss { get; set; }
public virtual ApplicationUser ApplicationUsers { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ base.OnModelCreating(modelBuilder);
modelBuilder.Entity<StoreModel>().HasKey(e => new { e.ApplicationUserId, e.BusinessId});
}
I have (2) entities, they are as follows:
[Table("User")]
public class User
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Required]
[MaxLength(20)]
public string Name { get; set; }
public ICollection<Role> Roles { get; set; }
public User()
{
this.Roles = new Collection<Role>();
}
}
[Table("User")]
public class Role
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int RoleId{ get; set; }
[Required]
[MaxLength(20)]
public string Name { get; set; }
public ICollection<User> Users { get; set; }
public Role()
{
this.Users = new Collection<User>();
}
}
This creates three tables in my database, User, Role and UserRole.
I am leveraging a generic repository, my Add and Update look like the following:
public virtual void Add(TEntity entity)
{
_dbSet.Add(entity);
}
public virtual void Update(TEntity entity)
{
_dbSet.Attach(entity);
_dbContext.Entry(entity).State = EntityState.Modified;
}
If I want to add a new User with Roles, I have to do the following in my UserRepository, which inherits from my generic repository.
public override void Add(User entity)
{
foreach (var role in entity.Roles)
_dbContext.Roles.Attach(role);
base.Add(entity);
}
This seems clunky, but it works.
My trouble is now when I want to update a User, say add a new Role. I thought I could do something similar.
public override void Update(User entity)
{
foreach (var role in entity.Roles)
_dbContext.Roles.Attach(role);
base.Update(entity);
}
But this does not work ... any ideas on what I am doing wrong would be appreciated!
Update
My use case is I have an existing User with X Roles, I add Y number of Roles, I want to update the User with the Y number of new Roles.
You shouldn't need to do that. If the role does not yet exist, you would do something like this:
var user = new User { Name="Fred" }
user.Roles.Add(new Role { Name="Accounting" });
context.Add(user);
If you are adding an existing role, then you need to get that role first
var role = context.Roles.Single(x => x.Name="Accounting");
var user = new User { Name="Fred" }
user.Roles.Add(role);
context.Add(user);
Here is my models :
public class User
{
public int UserId { get; set; }
public virtual ICollection<Shipment> Shipments { get; set; }
}
public class Shipment
{
public int ShipmentId { get; set; }
public int UserId {get; set;}
}
Any User May have 0~many shipments.I want to casccade the corresponded shipments and delete them automaticly when i delete an user.
I dont want make the relation BiDirrectional.and eneable DeleteOnCascade from other side by make the Virtual User Property [Requierd].
thanks.
You can achieve the above by overriding 'OnModelCreating' as follows.
public class DataContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Shipments).WithOptional().WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
}
Confusing Situation
I have a situation where I have 2 entities where 1 inherits from the other, that need to map to 2 separate tables, but code use should be around the base of the 2 entities.
Details
public class Team
{
public virtual int Id { get; set; }
public virtual ICollection<Employee> Members { get; set; }
}
public class Employee
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Team> Teams { get; set; }
}
public class EmployeeInfo : Employee
{
public virtual int Id { get; set; }
public virtual decimal Amount { get; set; }
}
We have an existing database schema where Employee and EmployeeInfo are separate tables with a FK between EmployeeInfo_Id and Employee_Id.
In our system "managers" will be adding Employee's to the system, with a set of private information (more properties than listed above) like pay, and add them to a Team. Other areas of the system will be using the Team or Employee objects for various other things. We would like to have to code super simple if the mapping can be done.
When a manager creates a new employee we would like the code to look something like this:
public void Foo(string name, decimal pay)
{
// create the employee
var employee = new EmployeeInfo();
employee.Name = name;
employee.Pay = pay;
// add him/her to the team
_team.Employees.Add(employee); // the idea being that consumers of the Team entity would not get the separate employee info properties
// save the context
_context.SaveChanges();
}
The end result would be that the EmployeeInfo properties entered into the EmployeeInfo table and the base Employee data is entered into the Employee table and added to the Team via the association table TeamEmployees.
So far I'm trying the current mappings, and I get an invalid column named "Discriminator." When just adding an employee to a team.
public class TeamConfiguration : EntityTypeConfiguration<Team>
{
public TeamConfiguration()
{
ToTable("Team");
HasKey(t => t.Id);
HasMany(t => t.Members).WithMany(m => m.Teams)
.Map(m =>
{
m.MapLeftKey("Team_Id");
m.MapRightKey("Employee_Id");
m.ToTable("TeamEmployees");
});
}
}
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
ToTable("Employee");
ToTable("EmployeeInfo");
HasKey(t => t.Id);
Property(p => p.Name);
HasMany(m => m.Teams)
.WithMany(t => t.Members)
.Map(m =>
{
m.MapLeftKey("Employee_Id");
m.MapRightKey("Team_Id");
m.ToTable("TeamEmployees");
});
}
}
Also, if I take the many-to-many between team and employee out of the mix I get a FK exception on Employee_Id to EmployeeInfo_Id.
Thanks, JR.
Discriminator is a column that's being added to your table when you use Table Per Hierarchy approach.
I think what you're looking for is "Table per Type (TPT)". Decorate your EmployeeInfo class as follows:
[Table("EmployeeInfo")]
public class EmployeeInfo : Employee
Or add below to your OnModelCreating event:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<EmployeeInfo>().ToTable("EmployeeInfo");
...
}
Or, create the following class and use it like modelBuilder.Configurations.Add(new EmployeeInfoConfiguration()); in OnModelCreating method:
public class EmployeeInfoConfiguration : EntityTypeConfiguration<EmployeeInfo>
{
public EmployeeInfoConfiguration()
{
ToTable("EmployeeInfo");
}
}
This will cause EF to create EmployeeInfo table with necessary constraints.
Also, it's good to initialize your collections in your objects' constructors to prevent null exception. For example in Team class:
public Team()
{
this.Employees = new HashSet<Employee>();
}
I copied your code exactly, and changed the following parts:
public class Team
{
public Team()
{
this.Members = new HashSet<Employee>();
}
public virtual int Id { get; set; }
public virtual ICollection<Employee> Members { get; set; }
}
public class Employee
{
public Employee()
{
this.Teams = new HashSet<Team>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Team> Teams { get; set; }
}
[Table("EmployeeInfo")]
public class EmployeeInfo : Employee
{
public virtual int Id { get; set; }
public virtual decimal Amount { get; set; }
}
In the DbContext, no changes:
public partial class TestEntities : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<EmployeeInfo> Employee_Info { get; set; }
public DbSet<Team> Teams { get; set; }
}
and your working Foo method:
public static void Foo(string name, decimal pay)
{
var _team = new Team();
var context = new TestEntities();
context.Teams.Add(_team);
// create the employee
var employee = new EmployeeInfo();
employee.Name = name;
employee.Amount = pay;
context.Employees.Add(employee);
context.SaveChanges();
// add him/her to the team
_team.Members.Add(employee);
// save the context
context.SaveChanges();
}
Finally, remove ToTable("EmployeeInfo"); part from EmployeeConfiguration since you have mentioned this correctly in your mode creating event.
For more info about Table Per Type approach, check out this great article.
I'm creating a POCO model to use with entity framework code first CTP5. I'm using the decoration to make a property map to a PK column. But how can I define a PK on more then one column, and specifically, how can I control order of the columns in the index? Is it a result of the order of properties in the class?
Thanks!
NOTE:
As of 2019 this answer became non-valid for later EntityFramework versions.
You can specify the column order in the attributes, for instance:
public class MyEntity
{
[Key, Column(Order=0)]
public int MyFirstKeyProperty { get; set; }
[Key, Column(Order=1)]
public int MySecondKeyProperty { get; set; }
[Key, Column(Order=2)]
public string MyThirdKeyProperty { get; set; }
// other properties
}
If you are using the Find method of a DbSet you must take this order for the key parameters into account.
To complete the correct answer submitted by Slauma, you can use the HasKey method to specify an order for composite primary keys as well:
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
}
public class Ctp5Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasKey(u => new
{
u.UserId,
u.Username
});
}
}
If, like me, you prefer to use a configuration file you can do that in this way (based on Manavi's example):
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
ToTable("Users");
HasKey(x => new {x.UserId, x.Username});
}
}
Obviously you have to add the configuration file to your context:
public class Ctp5Context : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserConfiguration());
}
}
Use as a anonymous object:
modelBuilder.Entity<UserExamAttemptQuestion>().ToTable("Users").HasKey(o => new { o.UserId, o.Username });