I have a simple User Class
public class User
{
public int ID { get; set; }
[Required]
public virtual ApplicationUser LoginID { get; set; }
[Required]
public string Name { get; set; }
public string JobTitle { get; set; }
[DefaultValue(UserRole.Standard)]
public UserRole Role { get; set; }
public virtual Company Company { get; set; }
public string Email { get { return LoginID.Email; } }
public bool HasAccess(UserRole TargetRole)
{
//Non-relevant logic
}
}
And I also have a Company class defined as
public class Company
{
public int ID { get; set; }
[Required]
[MaxLength(length: 70)]
public string Name { get; set; }
public virtual ICollection<User> Employees { get; set; }
public virtual ICollection<CompanyEmailDomain> Domains { get; set; }
public ICollection<User> Managers { get { return Employees.Where(x => x.Role == UserRole.Manager).ToList(); } }
}
However, when I run the add-migration command, it tries to add 3 Foreign keys on the User table to the Company table. Can anyone tell me why this would be the case?
AddColumn("dbo.Users", "Company_ID", c => c.Int());
AddColumn("dbo.Users", "Company_ID1", c => c.Int());
AddColumn("dbo.Users", "Company_ID2", c => c.Int());
Entity Framework simply counts the associations between User and Company. It detects three of them:
Company in User.
Employees in Company
Managers in Company
They're all 1-n (Company - User), so, EF concludes, User needs three foreign keys.
You know that Managers is a computed property. In fact, the property shouldn't even be be mapped. You should add the [NotMapped] attribute to it or map it as ignored by the fluent mapping API.
Also, you know that User.Company and Company.Employees are two ends of one association. But because of the two ICollection<User> properties, EF doesn't know which one to choose for the other end (the inverse end) of User.Company.
Now if you unmap Company.Managers, EF will see two properties --User.Company and Company.Employees-- and assume they belong together. So by unmapping one property, only one foreign key will be created.
Related
When inserting data into a many-to-many relationship, should you insert to the join-table or to both original tables?
My table models:
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<GroupMember> GroupMembers { get; set; }
The relationship between them is configured with Fluent API:
builder.Entity<GroupMembers>().HasKey(gm => new { gm.UserId, gm.GroupId });
builder.Entity<GroupMembers>().HasOne(gm => gm.Group).WithMany(group => group.GroupMembers).HasForeignKey(gm => gm.GroupId);
builder.Entity<GroupMembers>().HasOne(gm => gm.User).WithMany(user => user.GroupMembers).HasForeignKey(gm => gm.UserId);
public class Group
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public List<GroupMember> GroupMembers { get; set; } = new List<GroupMembers>();
}
public class User
{
[Key]
public Guid Id { get; set; }
public string Username { get; set; }
public List<GroupMembers> GroupMembers { get; set; } = new List<GroupMembers>();
}
public class GroupMembers
{
[Key]
public Guid UserId { get; set; }
public User User { get; set; }
[Key]
public Guid GroupId { get; set; }
public Group Group { get; set; }
}
Now, the question is; in which tables/classes should I insert the data about the group members?
Is it like this:
GroupMembers groupMember = new GroupMembers
{
Group = group,
GroupId = group.Id,
User = user,
UserId = user.Id
};
user.GroupMembers.Add(groupMember);
group.GroupMembers.Add(groupMember)
_databaseContext.Users.Update(user);
_databaseContext.SaveChanges();;
_databaseContext.Groups.Update(group);
_databaseContext.SaveChanges();
Or like this, leaving the User and Group untouched, with the information about their relationship ONLY in the join-table:
GroupMembers groupMember = new GroupMembers
{
Group = group,
GroupId = group.Id,
User = user,
UserId = user.Id
};
_databaseContext.GroupMembers.Add(groupMember);
_databaseContext.SaveChanges();
As far as Entity Framework is concerned, this is not a many-to-many relationship
What you have here is three entity types with two one-to-many relationships defined between them. You might know that this is done to represent a many-to-many, but EF doesn't know that.
If I arbitrarily change the names of your entities while maintaining the structure, you wouldn't be able to tell if this was a many-to-many relationship or not.
Simple example:
public class Country {}
public class Company {}
public class Person
{
public int CountryOfBirthId { get; set; }
public virtual Country CountryOfBirth { get; set; }
public int EmployerId { get; set; }
public virtual Company Employer { get; set; }
}
You wouldn't initially think of Person as the represenation of a many-to-many relationship between Country and Company, would you? And yet, this is structurally the same as your example.
Essentially, your handling of your code shouldn't be any different from how you handle any of your one-to-many relationships. GroupMembers is a table (db set) like any else, and EF will expect you to treat it like a normal entity table.
The only thing that's different here is that because GroupMember has two one-to-many relationships in which it is the "many", you therefore have to supply two FKs (one to each related entity). But the handling is exactly the same as if you had only one one-to-many relationship here.
In other words, add your groupMember to the table itself:
GroupMembers groupMember = new GroupMembers
{
// You don't have to fill in the nav props if you don't need them
GroupId = group.Id,
UserId = user.Id
};
_databaseContext.GroupMembers.Add(groupMember);
_databaseContext.SaveChanges();
Note: The following only applies to non-Core Entity Framework, as EF Core does not yet support it.
An example of what would be a "real" many-to-many relationship in (non-Core) EF would be if the intermediary table was not managed by you, i.e.:
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
}
public class Group
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User
{
[Key]
public Guid Id { get; set; }
public string Username { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
In this scenario, EF will still generate the cross table in the database, but EF will hide this table from you. Here, you are expected to work via the nav props:
var user = myContext.Users.First();
var group = myContext.Groups.First();
user.Groups.Add(group);
myContext.SaveChanges();
Whether you use a "real" many-to-many relationship or manage the cross table yourself is up to you. I tend to only manage the cross table myself when I can't avoid it, e.g. when I want additional data on the cross table.
Make sure the data id is correct and Exists
GroupMembers groupMember = new GroupMembers
{
GroupId = group.Id,
UserId = user.Id
};
_databaseContext.GroupMembers.Add(groupMember);
_databaseContext.SaveChanges();
There is less line of code and you have to assume that the object is completely independent when inserted
This is my first time working with database so I'm still trying to understand how all this works.
I'm trying to write my data (User, Note, Group) to database. I have three classes.
First one is User:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public List<Group> groups { get; set; }
public User()
{
this.groups = new List<Group>();
}
}
Note:
public class Note
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public string Content{ get; set; }
public string Password { get; set; }
public List<Group> groups { get; set; }
}
And Group:
public class Group
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
}
I know I should add foreign key but I don't know where and how. I'm really stuck so any help would be greatly appreciated!
Before you add foreign key. First you must define the relationship cardinality between each tables. To define it, you can just imagine how each table are represented in real life.
For example, a group can/must contain more than one user. And a user can be inside a single group or more at once.
So we define the relationship between a group and a user as MANY TO MANY, unless, you define it so that one user may only be registered to one group. In which case the relationship would become ONE TO MANY. With ONE being the group table and MANY refers to the user table.
Now, once the relationship is defined, usually the foreign key is set in the MANY table, in this case, it's the user table. So every user would have a foreign key called "Group".
As for the MANY TO MANY relationship the foreign key is set in both table. So that each table contains the key of the other.
I have a contact table
public class Contact : EntityBase
{
public int TrivialContactProperty { get; set; }
...
public virtual FiContact FiContact { get; set; }
public virtual PuContact PuContact { get; set; }
public virtual TrContact TrContact { get; set; }
}
and then a FiContact, PuContact, and TrContact table.
public class FiContact : EntityBase
{
public int TrivialFiProperty { get; set; }
...
public virtual Contact Contact { get; set; }
}
public class PuContact : EntityBase
{
public int TrivialPuProperty { get; set; }
...
public virtual Contact Contact { get; set; }
}
public class TrContact : EntityBase
{
public int TrivialTRProperty { get; set; }
...
public virtual Contact Contact { get; set; }
}
The contact table should have a zero or one relationship with all three other tables. So a contact can exist without any of the other three, or it can be related to one or two or all three of them.
Using fluent API I tried to configure this, after doing some research, and I came up with:
modelBuilder.Entity<FiContact>()
.HasRequired(r => r.Contact)
.WithOptional(o => o.FiContact);
modelBuilder.Entity<PuContact>()
.HasRequired(r => r.Contact)
.WithOptional(o => o.PuContact);
modelBuilder.Entity<TrContact>()
.HasRequired(r => r.Contact)
.WithOptional(o => o.TrContact);
But I am still getting the following error when I try to add a migration for this change:
FiContact_Contact_Source: : Multiplicity is not valid in Role 'FiContact_Contact_Source' in relationship 'FiContact_Contact'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '(asterisk symbol here)'.
PuContact_Contact_Source: : Multiplicity is not valid in Role 'PuContact_Contact_Source' in relationship 'PuContact_Contact'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '(asterisk symbol here)'.
TrContact_Contact_Source: : Multiplicity is not valid in Role 'TrContact_Contact_Source' in relationship 'TrContact_Contact'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '(asterisk symbol here)'.
From more research I saw that the primary key on the dependent entity is supposed to also be the foreign key? The only problem is that all of my entities inherit from a class "EntityBase" which defines common fields in all entities, including the primary key:
public abstract class EntityBase : IEntity
{
[Key]
public int Id { get; set;}
public bool IsActive { get; set; }
public DateTime CreatedOnDate { get; set; }
public int? CreatedByUserId { get; set; }
[ForeignKey("CreatedByUserId")]
public User CreatedByUser { get; set; }
public DateTime LastUpdatedOnDate { get; set; }
public int? LastUpdatedByUserId { get; set; }
[ForeignKey("LastUpdatedByUserId")]
public User LastUpdatedByUser { get; set; }
}
Is there a way to make this kind of table/entity relationship work with EF 6 Code First? Any help getting this type of relationship to work would be much appreciated.
I have created Entity Data Model in Visual Studio. Now I have file with SQL queries and C# classes generated from Model.
Question:
Classes are generated without annotations or code behind (Fluent API). Is it OK? I tried to run my application but exception was thrown:
Unable to determine the principal end of an association between the types 'Runnection.Models.Address' and 'Runnection.Models.User'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
I read that I can not use Fluent API with "Model First". So what can I do?
Code:
User
public partial class User
{
public User()
{
this.Events = new HashSet<Event>();
this.CreatedEvents = new HashSet<Event>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Photo { get; set; }
public int EventId { get; set; }
public string Nickname { get; set; }
public OwnerType OwnerType { get; set; }
public NetworkPlaceType PlaceType { get; set; }
public virtual ICollection<Event> Events { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Event> CreatedEvents { get; set; }
public virtual Owner Owner { get; set; }
}
Address
public partial class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string StreetNumber { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
public virtual User User { get; set; }
}
Context
//Model First does not use this method
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Address>().HasRequired(address => address.User)
.WithRequiredDependent();
modelBuilder.Entity<User>().HasRequired(user => user.Address)
.WithRequiredPrincipal();
base.OnModelCreating(modelBuilder);
}
You have to specify the principal in a one-to-one relationship.
public partial class Address
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public string Street { get; set; }
public string StreetNumber { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
public virtual User User { get; set; }
}
By specifying a FK constraint, EF knows the User must exists first (the principal) and the Address follows.
Further reading at MSDN.
Also, see this SO answer.
Updated from comments
In the designer, select the association (line between Users & Address). On the properties window, hit the button with the [...] on Referential Constraint (or double click the line). Set the Principal as User.
Error:
Had same error of "Unable to determine the principal end of an association between the types 'Providence.Common.Data.Batch' and 'Providence.Common.Data.Batch'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.".
HOWEVER, note that this is the SAME table.
Cause: My database was MS SQL Server. Unfortunately when MS SQL Server's Management Studio adds foreign keys, it adds the default foreign key as Batch ID column of Batch table linking back to itself. You as developer are suppose to pick another table and id to truly foreign key to, but if you fail to it will still allow entry of the self referencing FK.
Solution:
Solution was to delete the default FK.
Cause 2: Another situation is that the current table may be fixed but the old historical image of the table when the EF's edmx was done had the default FK.
Solution 2: is to delete the table from the Model Browser's Entity Types list and click "yes" and then "Update Model from the Database" again.
How do you represent a many-to-many relationship in the EF4 Code-First CTP3?
For example if I have the following classes:
class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Profile> Profiles { get; set; }
}
class Profile
{
public int Id { get; set; }
public string Name { get; set; }
}
In the database there is a UserProfiles table that has the FK for User and FK for Profile. How can I map this?
EDIT: I understand how to to currently map with having a ICollection<User> property on the Profile, but I really don't want to have a an opposite navigation property when it should be "Users have many profiles".
EDIT: CTP4 was released late yesterday (July 14 2010) and there is now support for this:
modelBuilder.Entity<Post>().HasMany(p => p.Tags).WithMany();
I found out finally that this currently isn't possible. Microsoft is looking to add this feature (only one navigation property).
See this link on the MSDN forums for more information: http://social.msdn.microsoft.com/Forums/en/adonetefx/thread/6920db2b-88c7-4bea-ac89-4809882cff8f
With many to many relationships you should include navigation properties on both sides and make them virtual (to utilize lazy loading)
class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Profile> Profiles { get; set; }
}
class Profile
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
Then with that setup you can define your many to many relationship (you can also let entity framework do it for you but I don't like the naming conventions it uses.)
modelBuilder.Entity<Profile>().
HasMany(p => p.Users).
WithMany(g => g.Profiles).
Map(t => t.MapLeftKey("ProfileID")
.MapRightKey("UserID")
.ToTable("UserProfiles"));
This will give you a table named UserProfiles with UserID and ProfileID as Keys.