I have two entities. Event and User. I want to save the creator (= user) of the event as contact in my Event entity. Additionally i want to have several guides (= user as well) associated with my event.
This is (part of) my code:
public class User
{
public int ID { get; set; }
public string LoginName { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
public class Event
{
public int ID { get; set; }
public string EventName { get; set; }
public User ContactName { get; set; }
public virtual ICollection<Room> Rooms { get; set; }
public virtual ICollection<User> Users { get; set; } // These are the guides
public virtual ICollection<Equipment> Equipments { get; set; }
[Required]
public EventType Type { get; set; }
}
I'm expecting EF6 (6.1.3) to create 1:n and n:m relationships between User and Event. What it does instead can be seen in the screenshot below:
There is one 1:n relation - as expected. But instead of one n:m relation there is one 1:n and one n:1 relationship. Please help.
For many to many link, You have to write it in the function :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany<Event>(s => s.Events)
.WithMany(c => c.Users)
.Map(cs =>
{
cs.MapLeftKey("UserRefId");
cs.MapRightKey("EventRefId");
cs.ToTable("UserEvent");
});
}
It's imply a new table between the two others
You can follow this guide
There isnt a foreign key relation mapped in Event class:
public class Event
{
public int ID { get; set; }
public string EventName { get; set; }
public int ContactId {get ; set;}
[ForeignKey("ContactId")]
public virtual User Contact { get; set; }
public virtual ICollection<Room> Rooms { get; set; }
public virtual ICollection<User> Users { get; set; } // These are the guides
public virtual ICollection<Equipment> Equipments { get; set; }
[Required]
public EventType Type { get; set; }
}
Related
i am designing a system and one of my entity has one to many relation as shown below.
public class Product
{
public int Id { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public Product Product { get; set; }
}
competitorProduct indicates that product has a equivalent which is sold by different store. should i define one-to-many relation as shown above or below? which one is correct?
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts{ get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
}
Assuming it is a one to many relationship (what would happen if a competitor product was competing with more than one of your products for example) you can do both and add in a foreign key as well.
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
You can then set up your relationship using fluent API as so:
modelBuilder.Entity<CompetitorProduct>(entity =>
{
entity.HasOne(e => e.Product)
.WithMany(e => e.CompetitorProducts)
.HasForeignKey(e => e.ProductId)
.HasConstraintName("FK_ComptetitorProduct_Product");
});
This way you can access the competitor products from the product and the product from the competitor products.
Here is a quick example of a ecommerce site I have worked on and how we did table relations.
I removed a bunch of the fields so you can see what you really need. Once to make relations and run Add-Migration EF will handle the FK constraints for you as long as you identified them in models like how I have below.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Active = true;
CreateDateTimeUtc = DateTime.UtcNow;
ModifiedDateTimeUtc = DateTime.UtcNow;
}
[StringLength(500)]
public string FirstName { get; set; }
[StringLength(500)]
public string LastName { get; set; }
[StringLength(1000)]
public string Address { get; set; }
[StringLength(100)]
public string Unit { get; set; }
[StringLength(250)]
public string City { get; set; }
[StringLength(25)]
public string State { get; set; }
[StringLength(20)]
public string ZipCode { get; set; }
//This will give access to a list of child carts a user could have
[Index]
public bool Active { get; set; }
public virtual ICollection<Cart> Carts { get; set; }
// Account Profile Image
public byte[] ProfileImage { get; set; }
[StringLength(500)]
public string ProfileFilename { get; set; }
[StringLength(100)]
public string ProfileMimeType { get; set; }
}
[Table("Cart", Schema = "dbo")]
public class Cart : AbstractTable
{
public Cart()
{
IsComplete = false;
}
//This create relation to user table where I can get one unique user.
[StringLength(128)]
[ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
//These link us to child tables of Cart where we can get a LIST of the items below
public virtual ICollection<CartCategory> CartCategories { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
// Marked when a payment/receipt is generated based off of this cart
public bool IsComplete { get; set; }
}
[Table("CartItem", Schema = "dbo")]
public class CartItem : AbstractTable
{
//This will return one unique cart id and let us access it as the parent record
[ForeignKey("Cart")]
public Guid CartId { get; set; }
public virtual Cart Cart { get; set; }
// Signifies if this was paid for in a receipt
public bool IsComplete { get; set; }
public virtual ICollection<CartItemCustomField> CustomFields { get; set; }
}
I reproduced a simple example of a problem I have with Entity Framework.
I want to have three tables:
Users, Projects, WorkOrders
Table Users has information about users for all other tables (in example only two). WorkOrders has information about which User has to work on this work order and to which Project it belongs.
Here are the classes:
public class User
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<WorkOrder> WorkOrders { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
public class Project
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
public DateTime Start { get; set; }
public virtual User Manager { get; set; }
public virtual ICollection<WorkOrder> WorkOrders { get; set; }
}
public class WorkOrder
{
[Key]
public int Id { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int AssigneeId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual User Assignee { get; set; }
}
When I try to run the program, it throws an exception:
'Introducing FOREIGN KEY constraint 'FK_dbo.WorkOrders_dbo.Projects_ProjectId' on table 'WorkOrders' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Then I went another way. I tried DB-first approach with EF. I first created the tables and connections in SQL Server Management Studio:
Then the generated models by EF look almost the same as mine, with code-first approach.
public partial class User
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public User()
{
this.Project = new HashSet<Project>();
this.WorkOrder = new HashSet<WorkOrder>();
}
public int Id { get; set; }
public string Name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Project> Project { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WorkOrder> WorkOrder { get; set; }
}
public partial class Project
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Project()
{
this.WorkOrder = new HashSet<WorkOrder>();
}
public int Id { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
public System.DateTime Start { get; set; }
public virtual User User { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WorkOrder> WorkOrder { get; set; }
}
public partial class WorkOrder
{
public int Id { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int AssigneeId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual User User { get; set; }
}
So the code is almost identical, except the SuppressMesages and constructors in WorkOrder and User classes. The second approach works.
I would like to know, what makes the difference? Also the context class is identical as my own. Where or how are defined this FK constraints or cascade delete settings?
It is unfortunate default behavior of EF code first to crate FK with on delete cascade. So while defining relations you need to simply change this setting:
//in context
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Project>()
.HasRequired<User>(s => s.User)
.WithMany()
.WillCascadeOnDelete(false);
Also there might be a convention for it in modelBuilder.Conventions if you want to just change behavior for all FK.
Given the following rough code-first schema, the goal would appear to be quite simple. An Invoice can either be from or to a Company, and the Invoices collection should contain all invoices regardless of which it is.
public class Company
{
public int Id { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public int FromCompanyId { get; set; }
public int ToCompanyId { get; set; }
public virtual Company FromCompany { get; set; }
public virtual Company ToCompany { get; set; }
}
You'll note in a Migration that a third Company_Id is generated for obvious reasons to support the Invoices navigation property as EF only appears to support a 1 Nav Prop -> 1 FK arrangement.
My question is whether or not it is possible to have the Invoices property contain both, or if I should map them individually (ie. IC<Inv> InvoicesFrom, IC<Inv> InvoicesTo) and create a client-side collection to have both manually.
I have tried:
Using InverseProperty on both FromCompany and ToCompany, which confuses EF as it can't determine the principal end of the relationship.
[ForeignKey(nameof(FromCompanyId)), InverseProperty(nameof(Company.Invoices))]
public virtual Company FromCompany { get; set; }
[ForeignKey(nameof(ToCompanyId)), InverseProperty(nameof(Company.Invoices))]
public virtual Company ToCompany { get; set; }
Using fluent API to map them, but it only takes into account the second which makes sense from a code perspective.
modelBuilder.Entity<Company>()
.HasMany(m => m.Invoices)
.WithRequired(m => m.ToCompany)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Company>()
.HasMany(m => m.Invoices)
.WithRequired(m => m.FromCompany)
.WillCascadeOnDelete(false);
There's of course no major issue if this isn't possible, I just could have sworn I've done it before.
For posterity, here is a complete version of the workaround to maintain an IEnumerable<Invoices> from company that contains both of the sets put together.
public class MyContext : DbContext
{
public MyContext() : base("DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Company>().HasMany(c => c.InvoicesFrom).WithRequired(i => i.FromCompany).WillCascadeOnDelete(false);
modelBuilder.Entity<Company>().HasMany(c => c.InvoicesTo).WithRequired(i => i.ToCompany).WillCascadeOnDelete(false);
}
public DbSet<Company> Companies { get; set; }
public DbSet<Invoice> Invoices { get; set; }
}
public class Company
{
public int Id { get; set; }
public virtual ICollection<Invoice> InvoicesFrom { get; set; }
public virtual ICollection<Invoice> InvoicesTo { get; set; }
[NotMapped]
public IEnumerable<Invoice> Invoices
{
get {
return InvoicesFrom.Union(InvoicesTo);
}
}
}
public class Invoice
{
public int Id { get; set; }
public int FromCompanyId { get; set; }
public int ToCompanyId { get; set; }
public virtual Company FromCompany { get; set; }
public virtual Company ToCompany { get; set; }
}
I'm running into an issue with Entity Framework, and this is the error: Unable to determine the principal end of the 'Force.Data.Models.Employee_Office' relationship. Multiple added entities may have the same primary key. I can't figure out what the issue is and I've been staring at it for three hours now. Here's the code, could someone point me in the right direction because I can't seem to:
Employee.cs
public partial class Employee : Person, IUser<int> {
public int Id { get; set; }
#region Relationship Properties
public byte CompanyId { get; set; }
public short OfficeId { get; set; }
public int? ManagerId { get; set; }
public virtual ICollection<Address> Addresses { get; private set; }
public virtual Company Company { get; set; }
public virtual ICollection<Device> Devices { get; private set; }
public virtual ICollection<Email> Emails { get; private set; }
public virtual ICollection<Employee> Employees { get; private set; }
public virtual Employee Manager { get; set; }
public virtual Office Office { get; set; }
public virtual ICollection<Phone> Phones { get; private set; }
public virtual ICollection<Role> Roles { get; private set; }
#endregion
}
Office.cs
public partial class Office {
public short Id { get; set; }
#region Relationship Properties
public int AddressId { get; set; }
public short RegionId { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Employee> Employees { get; private set; }
public virtual ICollection<Job> Jobs { get; private set; }
public virtual ICollection<Lead> Leads { get; private set; }
public virtual ICollection<Phone> Phones { get; private set; }
public virtual Region Region { get; set; }
#endregion
}
EmployeeConfiguration.cs
internal sealed class EmployeeConfiguration : EntityTypeConfiguration<Employee> {
public EmployeeConfiguration() {
this.ToTable("Employees");
this.HasKey(
k =>
k.Id);
#region Properties
#endregion
#region Relationships
/// Employee has a 1:* relationship with Offices.
this.HasRequired(
t =>
t.Office).WithMany(
t =>
t.Employees).HasForeignKey(
k =>
k.OfficeId);
#endregion
}
}
OfficeConfiguration.cs
internal sealed class OfficeConfiguration : EntityTypeConfiguration<Office> {
public OfficeConfiguration() {
this.ToTable("Offices");
this.HasKey(
k =>
k.Id);
#region Properties
this.Property(
p =>
p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
#endregion
#region Relationships
#endregion
}
}
Here's also a screenshot of the generated database, which looks fine to me. I don't think it's the database that's yelling at me, but rather EF beinc confused about something...
So, I'm an idiot, the problem was looking at me this whole time... It turned out it was the Seed method that was failing. In it I was adding 40 Employee objects, but one of them did not have an Office assigned to it and that's why it was failing. Ugh, I need a nap...
Here's the problem. I have table User which have quite a few fields. What I want to do is split this table into multiple entities like this:
User
-> GeneralDetails
-> CommunicationDetails
-> Address
etc.
All goes well when extracting some fields from User into GeneralDetails. However, when I try to do the same thing for CommunicationDetails EF blows up and require to establish one-to-one relationship between GeneralDetails and CommunicationDetails.
Sample entities definition:
public class User {
public int UserId { get; set; }
public string SomeField1 { get; set; }
public int SomeField2 { get; set; }
public virtual GeneralDetails GeneralDetails { get; set; }
public virtual CommunicationDetails CommunicationDetails { get; set; }
public virtual Address Address { get; set; }
}
public class GeneralDetails {
[Key]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public virtual User User { get;set; }
}
public class CommunicationDetails {
[Key]
public int UserId { get; set; }
public string Phone { get; set; }
public string DeviceToken { get; set; }
public virtual User User { get;set; }
}
public class Address {
[Key]
public int UserId { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Street { get; set; }
public virtual User User { get;set; }
}
Sample mapping:
modelBuilder.Entity<User>().
HasRequired(user => user.GeneralDetails).
WithRequiredPrincipal(details => details.User);
modelBuilder.Entity<User>().
HasRequired(user => user.CommunicationDetails).
WithRequiredPrincipal(details => details.User);
modelBuilder.Entity<User>().
HasRequired(user => user.Address).
WithRequiredPrincipal(details => details.User);
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<GeneralDetails>().ToTable("Users");
modelBuilder.Entity<Address>().ToTable("Users");
Why on earth EF want this relationship? Is there any way this could be solved?
The correct way to actually do this is by Complex Types rather than entities. Its actually a more common problem than you think.
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelbuilder.ComplexType<CommunicationDetails>();
modelbuilder.ComplexType<GeneralDetails>();
modelbuilder.ComplexType<Address>();
modelbuilder.Entity<User>().ToTable("Users");
}
}