Say I have two models - House and User
class User
{
[Key, Required]
public Guid UserId { get; set; }
public virtual House ILiveHere { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
}
And
class House
{
[Key, Required]
public Guid HouseId { get; set; }
[Required]
public virtual User HouseOwner { get; set; }
}
Many users can be associated with one house so it is a one-to-many relationship. However, there is also another relationship which is a one-to-one relationship between the owner of the house and the house. How would you define this with data annotations? Thanks
Give this a try... (this is EF Core)
public class MyDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<House> Houses { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"...");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<House>().HasMany(x => x.Residents).WithOne(x => x.House);
}
}
public class User
{
[Key, Required]
public Guid UserId { get; set; }
public virtual House House { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
}
public class House
{
[Key, Required]
public Guid HouseId { get; set; }
public List<User> Residents { get; set; }
[Required]
public virtual User HouseOwner { 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 have many classes representing tables, but three are giving me headaches: Person, Task, and Role, here is their code:
public class Person : BaseModel
{
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public string Title { get; set; }
public ICollection<TestEvent> TestEventsLed { get; set; }
public ICollection<TestEvent> TestEventsCreated { get; set; }
public ICollection<Program> ProgramsLed { get; set; }
public ICollection<Task> TasksCreated { get; set; }
public ICollection<PersonalEvent> PersonalEventsCreated { get; set; }
public virtual ICollection<Role> RolesHeld { get; set; }
public virtual ICollection<Task> TasksAssigned { get; set; }
}
public class Role : BaseModel
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> PeopleWithThisRole { get; set; }
}
public class Task : BaseModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime SuspenseDatetime { get; set; }
public DateTime CreatedDatetime { get; set; }
public int CreatedById { get; set; }
public bool Completed { get; set; }
public bool Archived { get; set; }
public Person CreatedBy { get; set; }
public virtual ICollection<Person> PeopleAssigned { get; set; }
}
What I end up with is mostly what I wanted, except a few hiccups:
Expected: Actual:
- People should have 0 foreign keys, just - People has 1 FK and 1 extra column out of
2 many-to-manys for RolesHeld and nowhere: Task_Id and the FK is for that
TasksAssigned new column referencing Id in Tasks?
- Task should have 1 foreign key for - Task has 2 extra columns out of nowhere
CreatedById linked to a Person called Person_Id and Person_Id1 and then
identical foreign keys attached to them
(and it has the expected CreatedById FK)
- There should be a RolePersons table - This part happened correctly and with the
with 2 FKs to represent the many-to-many correct FKs to represent the many-to-many
- There should be a TaskPersons table - There is no new table at all for this
with 2 FKs to represent the many-to-many
The weird thing is, I did some of these the same way (like the two many-to-many relationships) but then only 1 turned out correctly? Can you see what I did incorrectly?
Sometime default mapping is not what we want, so we have to explicitly say to EF what we need. Just add this method to your DbContext and it works as required:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().HasMany(p => p.TasksAssigned).WithMany(t => t.PeopleAssigned);
modelBuilder.Entity<Person>().HasMany(p => p.TasksCreated).WithRequired(t => t.CreatedBy).WillCascadeOnDelete(false);
}
Entity Framework do something by convention.
Look your Task class and Person class
public class Task : BaseModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime SuspenseDatetime { get; set; }
public DateTime CreatedDatetime { get; set; }
public int CreatedById { get; set; }
public bool Completed { get; set; }
public bool Archived { get; set; }
public Person CreatedBy { get; set; }
public virtual ICollection<Person> PeopleAssigned { get; set; }
}
public class Person : BaseModel
{
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public string Title { get; set; }
public ICollection<TestEvent> TestEventsLed { get; set; }
public ICollection<TestEvent> TestEventsCreated { get; set; }
public ICollection<Program> ProgramsLed { get; set; }
public ICollection<Task> TasksCreated { get; set; }
public ICollection<PersonalEvent> PersonalEventsCreated { get; set; }
public virtual ICollection<Role> RolesHeld { get; set; }
public virtual ICollection<Task> TasksAssigned { get; set; }
}
In your Task Class you are putting Person object and as well as a collection of Person.That's the thing is the cause of your headache i guess.
If you need many to many relation between them,then you should not put this property inside your Task Class
public Person CreatedById { get; set; }
public Person CreatedBy { get; set; }
Or If you need one to many relation between them,then Remove this property form your Task class
public virtual ICollection<Person> PeopleAssigned { get; set; }
Please can anyone help me!
I have a Model class named: Student.
Now i need to save a user with "StudentID". "StudentID" will be saved in user table as foreign key.
here is my Student class
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public int? DepID { get; set; }
public DateTime EnrollmentDate { get; set; }
[ForeignKey("DepID")]
public virtual Department Department { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
and my identity model is
public class ApplicationUser : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
so how can i add "studentID" into user table as foreign key.
In case you just want to use the StudenID as a foreign key in a different table, you can do it like this e.g
public class Employee
{
[Key]
public int EmployeeID { get; set; }
public string Name { get; set; }
public virtual EmployeeDetail EmployeeDetail { get; set; }
}
public class EmployeeDetail
{
[Key]
[ForeignKey("Employee")]
public int EmployeeID { get; set; }
public string Adress { get; set; }
public virtual Employee Employee { get; set; }
}
In case you are talking about the actual User table created by Asp.Net Identity, then you can simply extend and customize the User table:
http://typecastexception.com/post/2014/06/22/ASPNET-Identity-20-Customizing-Users-and-Roles.aspx
Hello had this problem before with another data type. For more details you can see here.
Multiplicity constraint violated Entity framework 5
But now that solution doesn't work or any other found on the net.
My classes are:
namespace Prometheus.Models
{
[Table("People")]
public class Person : IPerson, INamedEntity
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
[DisplayName("Prenume")]
public virtual string FirstName { get; set; }
[DisplayName("Nume")]
public virtual string LastName { get; set; }
public string Name
{
get
{
return FirstName + " " + LastName;
}
set { }
}
[DisplayName("Email")]
public virtual string Email { get; set; }
public DateTime? LastModified { get; set; }
public virtual ICollection<Result> Results { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
}
And
public class Project :INamedEntity
{
public int Id { get; set; }
public DateTime? LastModified { get; set; }
[DisplayName("Titlu Proiect")]
[Required]
public string Name { get; set; }//Title
[Required]
[DisplayName("Tip proiect")]
public virtual ProjectType ProjectType{ get; set; }
[DisplayName("Status proiect")]
public virtual ProjectStatus ProjectStatus { get; set; }
[Required]
[DisplayName("Tip program")]
public virtual ProgramType ProgramType { get; set; }
[Required]
[DisplayName("Numar luni")]
public int NumberOfMonths { get; set; }
[Required]
[DisplayName("Project title")]
public string NameEn { get; set; }
[Required]
[DisplayName("Acronim")]
public string Acronym { get; set; }
[Required]
[DisplayName("Numar contract")]
public string ContractNo { get; set; }
[Required]
[DisplayName("Domeniu de activitate")]
public virtual ActivityField ActivityField {get;set;}
[Required]
[DisplayName("Summary")]
public string SummaryEn { get; set; }
[Required]
[DisplayName("Rezumat")]
public string SumarryRo { get; set; }
[Required]
[DisplayName("Data inceput")]
public virtual DateTime StartDate { get; set; }
[Required]
[DisplayName("Data sfarsit")]
public virtual DateTime EndDate { get; set; }
[Required]
[DisplayName("Institutie coordonatoare")]
public virtual string CoordInstitution { get; set; }
[DisplayName("Valoare totala proiect")]
public decimal TotalValue { get; set; }
[DisplayName("Valuta")]
public virtual Currency Currency { get; set; }
[DisplayName("Rol in proiect")]
public virtual RoleInProject RoleInProject { get; set; }//coord proiect/partener/in afara inst
[DisplayName("Echipa proiect")]
public virtual ICollection<Person> People { get; set; }
[DisplayName("Director proiect")]
public virtual Person ProjectManager { get; set; }
public virtual ICollection<Institution> Partners { get;set;}
public virtual Person PersonInCharge { get; set; }
[DisplayName("Functie/Rol Expert In Proiect")]
public string UserFunctionInProject { get; set; }
[DisplayName("Pagina proiectului")]
[Required]
public string Website {get;set;}
public virtual ICollection<Result> Results { get; set; }
}
When i had the previous problem it was with a class Result and the same Person and was told that if i want a many-to-many relationship i have to add a Collection to Person and a Collection to Result and that did the trick.
But now as you can see i already have
public virtual ICollection<Person> People { get; set; } in Project
and
public virtual ICollection<Project> Projects { get; set; } in Person
And every time i try to add more than one User to the project i get the error in that title when calling the Save() method from DbContext
I have no idea what to do.
Thanks.
Accidentally posted the wrong class, it was the result instead of the Person, now it's correct
Update 1
public void AddProjectForUsers(IEnumerable<int> userIds, Project project)
{
foreach (var id in userIds)
{
AddProjectForUser(id,project);
}
}
public void AddProjectForUser(int userId, Project project)
{
var user = _ctx.Users.SingleOrDefault(u => u.Id == userId);
if (user != null)
{
if (!user.Projects.Contains(project))
{
user.Projects.Add(project);
}
}
}
public bool Save()
{
return _ctx.SaveChanges() > 0; // the error is thrown here
}
The 3 are from my repository class
And the call to them is
Repo.AddProjectForUsers(team, project);
Repo.Save();
Where team is an IEnumerable<int> and project is of type Project
It looks like you may need the InverseProperty data annotation attribute to do this:
namespace Prometheus.Models
{
[Table("People")]
public class Person : IPerson, INamedEntity
{
...
[InverseProperty("People")]
public virtual ICollection<Project> Projects { get; set; }
}
}
public class Project :INamedEntity
{
...
[DisplayName("Echipa proiect")]
[InverseProperty("Projects")]
public virtual ICollection<Person> People { get; set; }
}
The exception message you are receiving is very telling. Since EF is complaining that the relation is expected to be a one-to-zero-or-one, this leads me to believe that EF is confusing the People - Projects association with either the PersonInCharge - Project or ProjectManager - Project association.
One way to solve this would be to expose foreign key properties on your Project entity, which you should be doing anyway - you can get weird transient errors when you don't. Another way would be to use the fluent API (a.k.a. model builder) instead of data annotations.
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");
}
}