I have the following construct; a person with an address (ownsone, sub-entity) and an address with a country (hasone, one-to-one)
public class Person
{
public Guid Id {get; set;}
public string Name {get; set;}
public Address Address {get; set;}
}
public class Address
{
public Guid Id {get; set;}
public Guid CountryId {get; set;}
public Country Country {get; set;}
}
public class Country
{
public Guid Id {get; set;}
public string CountryCode {get; set;}
}
public class EntityConfiguration<Person> where Person : class
{
public void Configure(EntityBuilder<Person> builder)
{
builder.OwnsOne(p => p.Address, addressBuilder =>
addressBuilder.HasOne(address => address.Country).WithOne();
}
}
When I run Add-Migration I get a block of code for each entity which owns one address. With auto-generated keys and so on. But I want to specify the relation explicitly with HasForeignKey. How to do this?
EF Core provides 2 fluent APIs for one-to-one relationships - HasForeignKey and HasPrincipalKey.
The main difference with one-to-many APIs is that you need to explicitly provide the generic type argument, because the principal and dependent end of the relationship cannot be determined by the HasOne / WithOne calls (for one-to-many the one is always the principal and many is the dependent). The navigation properties in this case doesn't matter:
addressBuilder.HasOne(address => address.Country)
.WithOne()
.HasForeignKey<Address>(address => address.CountryId)
Reference: Relationships
Related
I am working on a project which involves EF Core.
I would like to use the foreign keys from Category with one single navigation property.
Therefore, Item stores the foreign keys of Category, and the names of the Category can be shown.
This is how the relationship looks like:
Classes:
[Table("Item" , Schema = "public")]
public class Item
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ItemID {get; set;}
public string Name {get; set;}
public int CategoryID {get; set;}
//Single Navigation Property
public Category Category {get; set;}
}
[Table("Category" , Schema = "public")]
public class Category
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CategoryID {get; set;}
public string Name {get; set;}
}
DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>().ToTable("Item");
modelBuilder.Entity<Category>().ToTable("Category");
modelBuilder.Entity<Item>()
.HasOne(i=>i.Category)
.WithOne()
.HasForeignKey<Item>(i=>i.CategoryID);
}
It shows an error
Duplicate key value violates unique constraint "IX_ItemDB_CategoryID"
Apparently, it means CategoryID cannot be duplicate in Item.
What am I doing wrong here? Thanks!
actually your answer in this article. What you need to do is change the location of the navigation property. So Item should not have an Category property, Category should have a collection of Item for single navigation. the article describes other ways.
In case I understand properly You need one to many relation and I can offer you that structure and if you use Migration your tables will be properly build in the database. No to do anything in the FluentApi to, Your FK, PK(identity) and Indexes will be automatically created
[Table("Item" , Schema = "public")]
public class Item
{
public int Id {get; set;}
public string Name {get; set;}
public int CategoryId {get; set;}
public Category Category {get; set;}
}
[Table("Category" , Schema = "public")]
public class Category
{
public Category()
{
this.Items = new HashSet<Item>();
}
public int Id {get; set;}
public string Name {get; set;}
public virtual ICollection<Item> Items {get; set;}
}
Ivan Stoev is correct. The model of my question is fine and fluent configurations are not required. EFCore will handle the rest.
I am trying to work out how to add a reference an object from within EF.
I little hard to explain so I thought I would share a dotnetfiddle example
https://dotnetfiddle.net/4n5Flk
public class ReportConfig
{
[Key]
public int ReportConfigKey {get; set;}
public int ConfigTypeKey {get; set;}
public int ConfigValue {get; set;}
[ForeignKey("ConfigTypeKey")]
public virtual <ConfigType> ConfigType {get; set;}
}
public class ConfigType
{
[Key]
public int ConfigTypeKey {get; set;}
//This can be
//Address
//Car etc etc.
public string ConfigName {get; set;}
}
public class Car
{
[Key]
public int CarId {get; set;}
//other items
}
public class Address
{
[Key]
public AddressId {get; set;}
}
In a nutshell I am trying to create a navigation property to either a car or an address based on the ConfigValue and the ConfigTypeKey
I was thinking to create 2 navigation properties "Car" and "Address" and for both add 2 foreign key's 1 to the object and 1 as a hardcoded reference to ConfigName as Car or Address
is this possible atall
Sorry for the poor explanation.
I have two projects that depend on two different db contexts. The projects use EF code first. Te thing is the contexts have multiple(5 to be exact) common tables. So the use the same class to create the entity. The problem is that now this class needs to change(columns/propertys to be added) for one of the contexts, but to stay the same for the other.
So for example I have:
public interface IPeopleFirstGroupDbContxt
{
IDbSet<Person> Person {get; set;}
IDbSet<Grade> Grades {get; set;}
IDbSet<Book> Books {get; set;}
}
public interface IPeopleSecondGroupDbContxt
{
IDbSet<Person> Person {get; set;}
IDbSet<Grade> Grades {get; set;}
IDbSet<Book> Books {get; set;}
IDbSet<Hobbie> Hobbies {get; set;}
IDbSet<School> Schools {get; set;}
}
public class Person(
{
public Person()
{
this.Grades = new HashSet<Grade>();
this.Books= new HashSet<Book>();
}
[Key]
public int PersonId {get; set;}
public string FirstName {get; set;}
public ICollection Grades {get; set;}
public ICollection Books{get; set;}
}
For the IPeopleSecondGroupDbContxt db context in table Person new one to many relationship to Hobbies shoud be added without changing the IPeopleFirstGroupDbContxt and Person table in IPeopleFirstGroupDbContxt.
- In this case I can't make Person class abstract and inherit it because this one to many relationship doesn't work. It just creates new column in Hobiie class, but doesn't exactly create the relationship.
- Second chance is just to inherit from Person -> NewPerson, without making it abstract, but this will create relationship between these two tables by PersonId
Any ides?
You are right, relational databases don't handle inheritance very well, and thus entity framework has to work around it if you want to use inheritance.
The problem is that your DbSet classes hold the properties that represent the columns in the tables (the real information), but also the properties that represent the relationships between the tables: the ICollection and the foreign key.
Apparently a Grade has a one-to-many relation to a Person: every Person has zero or more Grades, every Grade belongs to exactly one Person.
In your other database you don't have Persons, you have ExtendedPersons. ExtendedPersons are very similar to Persons, for instance, every ExtendedPerson has zero or more Grades. However, ExtendededPersons have something more. ExtendedPersons also have Hobbies and Books. Because of the similarity with Persons you decided to use inheritance.
In your other database a Grade is different than a Grade in your first database. A Grade is not a Grade of a Person, it is a grade of an ExtendedPerson.
If you don't want to create a separate ExtendedGrade class, you need fluent API to inform entity framework about this different relation. The best method, which won't interfere with your basic classes is the use of fluent API in OnModelCreating
The Persons:
class Person
{
public int Id {get; set;}
// every Person has zero or more PersonGrades (one-to-many
public virtual ICollection<Grade> Grades {get; set;}
... // other properties
}
class ExtendedPerson : Person
{
// inherits primary key and Grades from Person
// every ExtendedPerson has zero or more Hobbies (one-to-many)
public virtual ICollection<Hobby> Hobbies {get; set;}
...
}
Grades and Hobbies:
class Grade
{
public int Id {get; set;}
// every Grade belongs to exactly one (subclass of) Person using foreign key
public int PersonId {get; set;}
public virtual Person Person {get; set;}
}
class Hobby
{
public int Id {get; set;}
// every Hobby belongs to exactly one ExtendedPerson using foreign key
public int ExtendedPersonId {get; set;}
public virtual ExtendedPerson ExtendedPerson {get; set;}
}
Your first DbContext:
public MyDbContext : DbContext
{
public DbSet<Person> Persons {get; set;}
public DbSet<Grade> Grades {get; set;}
public override void OnModelCreating(...)
{
// a person has zero or more Grades,
// every Grade belongs to exactly one Person
// using foreign key PersonId
modelBuilder.Entity<Person>()
.HasMany(person => person.Grades)
.WithRequired(grade => grade.Person)
.HasForeignKey(grade => grad.PersonId);
}
}
Your other DbContext:
public MyOtherDbContext : DbContext
{
public DbSet<ExtendePerson> ExtendedPersons {get; set;}
public DbSet<Grade> Grades {get; set;}
public DbSet<Hobby> Hobbies {get; set;}
public override void OnModelCreating(...)
{
// every extended person has zero or more Grades
// every Grade belongs to exactly one Person
// using foreign key PersonId
modelBuilder.Entity<ExtendedPerson>()
.HasMany(extendedPerson => extendedPerson.Grades)
.WithRequired(grade => grade.Person)
.HasForeignKey(grade => grade.PersonId);
// every extended person has zero or more hobbies
// every hobby belongs to exactly one ExtendedPerson
// using foreign key ExtendedPersonId
modelBuilder.Entity<ExtendedPerson>()
.HasMany(extendedPerson => extendedPerson.Hobbies)
.WithRequired(hobby => hobby.ExtendedPerson)
.HasForeignKey(hobby => hobby.ExtendedPersonId);
// proper table name for hobbies:
modelBuilder.Entity<Hobby>().ToTable("Hobbies");
}
}
This issue has been dogging me for a while, and would appreciate any help
Let's say I have two classes, users and room, that basically go like this
public class User
{
[Key]
public int UserId {get; set;}
public string Username {get; set;}
public virtual List<Room> Rooms {get; set;}
}
public class Room
{
[Key]
public int RoomId {get; set;}
[ForeignKey("Owner")] // have tried with and without the explicit relationship
public int OwnerId {get; set;}
public User Owner {get; set; }
public virtual List<User> Members {get; set;}
}
My hope is to build s structure that allows a user to have multiple rooms where they are the owner, but also be included into multiple rooms as a member.
The problem comes when I actually try to make a room and add the members. The owner properties are added, but the Members list is always empty.
I'm using the annotation API, but have also tried the fluent API
When I try this, I get a multiplicity error
modelBuilder.Entity<Room>()
.HasMany(t => t.Members).WithMany(u => u.Rooms);
I am sure the answer is simple, I am just not that good with entity. Any help would be appreciated.
Thanks
You have two relationships between User and Room:
Rooms where user is the owner (one-to-many)
Rooms where user is a member (many-to-many)
Single Rooms collection cannot represent the two relationships, you need two collections:
public class User
{
[Key]
public int UserId {get; set;}
public string Username {get; set;}
public virtual List<Room> OwnerOfRooms {get; set;}
public virtual List<Room> MemberOfRooms {get; set;}
}
and use the following Fluent configuration:
modelBuilder.Entity<User>()
.HasMany(u => u.OwnerOfRooms)
.WithRequired(r => r.Owner)
.HasForeignKey(r => r.OwnerId);
modelBuilder.Entity<User>()
.HasMany(u => u.MemberOfRooms)
.WithMany(r => r.Members);
Note that the second relationship will create automatic junction table UserRooms.
If you are using EF Core, to create a Many-to-Many, which is what you're after, you'll need to have another entity, UserRoom, which contains a 1-to-Many with Users and 1-to-Many with Room, like:
public class UserRoom
{
public int UserId { get; set; }
public User User { get; set; }
public int RoomId { get; set; }
public Room Room { get; set; }
}
Then in User:
[InverseProperty(nameof(UserRoom.User))]
public ICollection<UserRoom> UserRooms { get; set; }
and similarly in Room:
[InverseProperty(nameof(UserRoom.Room))]
public ICollection<UserRoom> UserRooms { get; set; }
In your case, it sounds like you want two many-to-many relationships - one for room membership and one for room ownership. In that case, you'll need to implement this pattern twice, with linking entities perhaps more appropriately named OwnerRoom and MemberRoom.
If room ownership is unique to a single user, then you'll need MemberRoom and the pattern above along with a standard 1-to-many relationship with Room and User, like you've already implemented, just decorate User.Rooms with:
[InverseProperty(nameof(Room.Owner))]
I have two entities: Employee & Contract.
In the Contract entity I have the properties AddedByEmployee & AssignedToEmployee.
I want a collection navigation property in my Employee class but how do I reference the correct key in the Contract class?
So far I have:
public class Employee
{
public int EmployeeID {get; set;}
public string Name {get; set;}
private readonly ObservableListSource<Contract> _Contracts = new ObservableListSource<Contract>();
public virtual ObservableListSource<Contract> Contracts { get { return _Contracts; }
}
public class Contract
{
public int ContractID {get; set;}
public string Name {get; set;}
public int AddedByEmployeeID {get; set;}
public int AssignedToEmployeeID {get; set;}
[ForeignKey("AddedByEmployeeID")]
public virtual Employee AddedByEmployee { get; set; }
[ForeignKey("AssignedToEmployeeID")]
public virtual Employee AssignedToEmployee { get; set; }
}
So basically: How do I let the ObservableListSource<Contract> know it is the AddedByEmployeeID I want to map to?
Thanks
You can do this by either using DataAnnotations or the Fluent API.
Doing it with DataAnnotations you can add the InverseProperty attribute to either your Contracts or AddedByEmployee property (or both, but that is not necessary).
This will tell Entity Framework that a relationship should be made between Contracts and AddedByEmployee.
[InverseProperty("AddedByEmployee")]
public virtual List<Contract> Contracts
{
get { return _Contracts; }
}
If you want to use the Fluent API instead, you simply have to explicitly define the relationship like this:
modelBuilder.Entity<Employee>()
.HasMany(p => p.Contracts)
.WithRequired(p => p.AddedByEmployee)