Entity Framework - Relationship confusion - sql

I'm having problems understanding Entity Framework Code Firsts relationship creation, as I'm more used to the traditional way.
The one-to-many relationship seems clear to me: The childs only need a foreignKey ID property indicating to which Parent they belong.
public class Parent
{
public int Id { get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Now, I'm not quite sure how to properly create a many-to-many relationship. Probably, there is an additional table ParentChild necessary, so there is no need for (foreign-key) ID properties right?
public class Parent
{
public int Id { get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
public virtual ICollection<Parent> Parents { get; set; }
}
Now, for the one-to-one relationship, I have no clue.
public class Parent
{
public int Id { get; set; }
public int ChildID { get; set; }
public virtual Child child { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Are the foreign ID properties even needed or can I just have a Child property in the Parent class and a Parent-type property in the Child Class? And is the virtual keyword allowed when I ommit the foreign key ID properties?

I suggest you to have a look in the entity framework fluent api. One to one relation can be easily achieved with the fluent api. Explanation source. For fast reference:
public class Student
{
public Student() { }
public int StudentId { get; set; }
[Required]
public string StudentName { get; set; }
[Required]
public virtual StudentAddress StudentAddress { get; set; }
}
public class StudentAddress
{
[Key, ForeignKey("Student")]
public int StudentId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public virtual Student Student { get; set; }
}
You can override OnModelCreating in your datacontext class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentAddress>()
.HasKey(e => e.StudentId);
modelBuilder.Entity<StudentAddress>()
.Property(e => e.StudentId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<StudentAddress>()
.HasRequired(e => e.Student)
.WithRequiredDependent(s => s.StudentAddress);
base.OnModelCreating(modelBuilder);
}

With Entity Framework, you don't even have to specify the foreign key relations as it will deduce it from your model and create the tables accordingly. The only relation type you actually have to do something for is the 0..1 or 1 to 0..1 or 1.
Don't forget that the object model is way more permissive than the database model. You can have a property storing a collection in an object but not in a table.
You have to think differently as EF will do the work for you at the databse level and you will have access to all properties defines in your object model, even collection properties.
The rules I always use to get it done is the following :
If the cardinality of the relation is 0..1 or 1, use a reference to the other entity object as your property. If the cardinality is many, use a collection.
Here are some use cases :
1 to Many (many childs per parent) :
public class Parent
{
public int Id { get; set; }
// Navigation property
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
// Navigation property
public virtual Parent Parent { get; set; }
}
The result in the database would be a Table Parent with a single property (Id) and a table Child with two properties, the Id and the foreign key property automatically generated named Parent_Id (Table name then underscore, then the key property of the related class).
The Many to Many :
public class ClassA
{
public int Id { get; set; }
// Navigation property
public virtual ICollection<ClassB> ClassBs { get; set; }
}
public class ClassB
{
public int Id { get; set; }
// Navigation property
public virtual ICollection<ClassA> ClassAs { get; set; }
}
The result in the database would be a Table ClassA with a single property (Id), a table ClassB with a single property (Id) and a third table (the relation table for the many-to-many relation) with two properties (this Ids of both tables).
EF will deduce what it needs to in order to do the job so you don't have to be more specific then that.
Now for the only somewhat problematic one, the 1 to 1 :
public class ClassA
{
public int Id { get; set; }
// Navigation property
public virtual ClassB ClassB { get; set; }
}
public class ClassB
{
public int Id { get; set; }
// Navigation property
public virtual ClassA ClassA { get; set; }
}
Following the rule I gave at the beginning, this is what we would do. But in this case, EF has no way to know the direction of the relation... A 1-to-1 coule be either direction. We will have to let it know the direction using annotation (to me the easiest way compared to Fluent API).
public class ClassA
{
public int Id { get; set; }
// Navigation property
public virtual ClassB ClassB { get; set; }
}
public class ClassB
{
[ForeignKey("ClassA")]
public int Id { get; set; }
// Navigation property
public virtual ClassA ClassA { get; set; }
}
The annotation [ForeignKey("ClassA")], in ClassB tells EF to use the Id column from ClassB as the foreign key in ClassA.
The result in the database would be a Table ClassA with 2 properties (Id and ClassB_Id) and a table ClassB with a single property (Id).
You don't have to create the foreigh key properties yourself as EF will do it for you.

Related

EF Core only creates one self-referencing foreign key instead of 2

I am trying to create a model to represent missions in a game. When a mission is completed, another mission is played depending on which team won the mission. For example Mission1, if TeamA wins then you play Mission2, if TeamB wins then you play Mission3.
For each mission I want two self-referencing columns, TeamAWinMission and TeamBWinMission, which hold the foreign key to another mission.
The migrations only seem to recognise a single self-reference in the model.
This will create a column for TeamAWinMission:
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public Mission TeamAWinMission{ get; set; }
}
This will only create a column for TeamBWinMission:
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public Mission TeamAWinMission{ get; set; }
public Mission TeamBWinMission{ get; set; }
}
How can I make the migration generate columns/foreign keys for both of these?
Edit: Was able to resolve this with InverseProperty from https://stackoverflow.com/a/46832490/11575271
It seems that there is one-to-one self-reference relationship in Mission model , you could try to define the [ForeignKey]in your model like below :
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("TeamAWinMission")]
public int? TeamAWinMissionId { get; set; }
public Mission TeamAWinMission { get; set; }
[ForeignKey("TeamBWinMission")]
public int? TeamBWinMissionId { get; set; }
public Mission TeamBWinMission { get; set; }
}
Then use Include attribute to load related data in the controller :
public IActionResult Mission()
{
var result = _context.Missions
.Include(m => m.TeamAWinMission)
.Include(m => m.TeamBWinMission)
.ToList();
return View(result);
}
Reference : https://learn.microsoft.com/en-us/ef/core/querying/related-data

Entity framework code first, delete childs by updating parent

As entity framework states, "Code first", here we go with the code first...
public class BaseModel
{
[Key]
public Guid Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
public BaseModel()
{
this.Id = Guid.NewGuid();
this.DateCreated = DateTime.Now;
this.DateChanged = DateTime.Now;
}
}
public class Association: BaseModel
{
public string Name { get; set; }
public string Type { get; set; }
public virtual List<Rule> Rules { get; set; }
public Association()
: base()
{
}
}
public class Rule: BaseModel
{
[ForeignKey("Association")]
public Guid AssociationId { get; set; }
//[Required]
public virtual Association Association { get; set; }
//[Required]
public string Name { get; set; }
public string Expression { get; set; }
public virtual List<Action> Actions { get; set; }
public Rule()
: base()
{
}
}
public class Action: BaseModel
{
public string Name { get; set; }
public string ActionType { get; set; }
[ForeignKey("Rule")]
public Guid RuleId { get; set; }
public virtual Rule Rule { get; set; }
public int Order { get; set; }
public Action()
: base()
{
}
}
So these are my four model classes that are using entity framework code first.
Each inherit from the baseclass, so they all have an Id Guid as Primary Key.
An Association has a list of rules. (Rule has FK to Association)
A Rule as has a list of actions. (Action has FK to Rule)
What I would like to do is only change and save the most upwards class = Association.
For example when deleting a rule, I would like this code to work:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
Association ass = this.DataContext.Associations.FirstOrDefault(a => a.Id == assId);
ass.Rules.RemoveAll(r => r.Id == ruleId);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}
On the context.savechanges this is giving me this error:
'The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.'
This error also occurs when deleting an action.
Is there a way to change the most upper (Association) object AND ONLY changing things to this Association.
I DO NOT want to say context.Rules.remove(...) or context.actions.remove(...)
here's the source: http://server.thomasgielissen.be/files/mvctesting.zip
you need VS2012, all nuget packages are included in zip and you should be able to build and run the project.
Thanks in advance for your feedback!
Greetz,
Thomas
I you want to fix this issue, you should store your relations through junction tables. I don't think that you can achieve what you need, with this model.
However if you put a junction table(or entity) between your entities, you can easily remove child objects and update parent object.
For example, put a junction entity between Association and Rule:
public class AssociationRule: BaseModel
{
public Guid AssociationId { get; set; }
public Guid RuleId { get; set; }
[ForeignKey("AssociationId")]
public virtual Association Association { get; set; }
[ForeignKey("RuleId")]
public virtual Rule Rule { get; set; }
public Association()
: base()
{
}
}
Now, you can easily remove any rule from any association:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
AssociationRule assr = this.DataContext
.AssociationRuless
.FirstOrDefault(ar => ar.AssociationId == assId && ar.RuleId == ruleId);
this.DataContext.AssociationRules.Remove(assr);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}

One to one relationship - code first

I'm trying to make one to one relationship. I don't want to use fluent API if it is not necessary. This is what I tried so far:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Person
{
public int Id { get; set; }
[ForeignKey("UserId")]
public UserProfile UserProfile { get; set; }
}
Yes, I know there are few similar questions outhere, but none of them were short and clear. Lot of them also did not work.
It depends a little on what type of table structure you want to achieve. There are various ways of doing this, and there is a good walkthrough for all the options, from Shared Primary Key Assocations to One-to-One Foreign Key Associations in those links. Unfortunately those links make more use of Fluent than Annotations. The samples below use Annotations, as you need.
Shared Primary Key
In theory the Shared Primary Key (horizontal table partitioning, in database terms) is the "correct way". It is also the smallest change you need to do to be able to generate a migration (which will use a Shared Primary Key Association). Note that I would change Person.Id to Person.UserId to better show your intent:
// tested in EF 5 and MVC 4.5.
[Table("UserProfile")]
public class UserProfile {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
[Table("Person")] // not required, added for clarity in sample code
public class Person {
// Note the change of property name to reflect that this is a shared primary key,
// using the UserId column in UserProfile as the Primary Key
[Key]
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }
}
// The generated migration:
public partial class AddTable_Person : DbMigration
{
public override void Up() {
CreateTable(
"dbo.Person",
c => new {
UserId = c.Int(nullable: false),
})
.PrimaryKey(t => t.UserId)
.ForeignKey("dbo.UserProfile", t => t.UserId)
.Index(t => t.UserId);
}
public override void Down(){
DropIndex("dbo.Person", new[] { "UserId" });
DropForeignKey("dbo.Person", "UserId", "dbo.UserProfile");
DropTable("dbo.Person");
}
}
this then gives you, in effect a 1:0-1 relationship between UserProfile (which is mandatory) and People (which is optional, but can have one per person at the most.
If you want to use Id in Person then do the following (the migration will change accordingly):
public class Person {
public int Id { get; set; }
[ForeignKey("Id")]
public UserProfile UserProfile { get; set; }
}
Shared Primary Key with two-way navigation
If you want to navigate from UserProfile to Person you have more work to do. Simply adding public virtual Person Person { get; set; } to UserProfile will give you an error:
Unable to determine the principal end of an association between the types 'Test.Models.UserProfile' and 'Test.Models.Person'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
So, we fix it with [Required] on the Person.UserProfile property (Person requires UserProfile). This gives the same migration as before.
// tested in EF 5 and MVC 4.5.
[Table("UserProfile")]
public class UserProfile {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
[ForeignKey("UserId")]
public virtual Person Person { get; set; }
}
[Table("Person")] // not required, added for clarity in sample code
public class Person {
[Key]
public int UserId { get; set; }
[ForeignKey("UserId")]
[Required]
public virtual UserProfile UserProfile { get; set; }
}
Again, this works if you use Id for Person instead of UserId:
public class Person {
[Key]
public int Id { get; set; }
[ForeignKey("Id")]
[Required]
public virtual UserProfile UserProfile { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual Person Person {get;set;}
}
public class Person
{
public int Id { get; set; }
public int UserProfileUserId { get; set; } //Foreign Key
public virtual UserProfile UserProfile { get; set; }
}
If you want to create a one-to-one relationship the first thing you must clarify is what is the principal and what the dependent entity in this relationship. Can a Person exist without UserProfile or can a UserProfile exist without Person?
Because you have started to apply a [ForeignKey] attribute in Person I am assuming now that Person is the dependent, i.e. it cannot exist without UserProfile.
Correct application of the [ForeignKey] attribute would then be:
public class Person
{
[ForeignKey("UserProfile")]
public int Id { get; set; }
public UserProfile UserProfile { get; set; }
}
I am not sure if that is sufficient because you don't have a Person navigation property in UserProfile. If it doesn't work add this property to UserProfile:
public Person Person { get; set; }
If you don't want to have such a navigation property you can't avoid Fluent API:
modelBuilder.Entity<Person>()
.HasRequired(p => p.UserProfile)
.WithOptional();

How to build a self referencing table with composite key in fluent api and EF

I'm building a hierarchical Database with "closure table" to build the tree
It is a self referencing table, and the two keys should become the primary key.
The Problem is, I end up with 5 columns, when I expect only 3.
Here is what I tried:
public class Tree
{
public int TaskId { get; set; }
public Task Task { get; set; } //navigation Property to TaskTable
public int? ChildId { get; set; }
public Tree Child { get; set; } //navigation Property
public int Length { get; set; } //Length
}
public class Task
{
public int TaskId { get; set; }
public virtual ICollection<Tree> Trees { get; set; }
}
modelBuilder
.Entity<Task>()
.HasKey(t => t.TaskId);
modelBuilder
.Entity<Tree>()
.HasKey(a => new { a.TaskId, a.ChildId });
The result is a table with 5 columns:
TaskId
ChildId
Length
Child_TaskId
Child_ChildId
I expected:
TaskId
ChildId
Length
I'm guessing some fluent api missing, but I couldn't get to work otherwise ?

PetaPoco to return self-referencing hierarchy

How would one write a query/method to return a POCO that is from a self-referencing database as shown in this question
Firstly you would map it a flat class. eg. db.Fetch<CategoryDb>("select * from categories");
public class CategoryDb {
public int Id { get; set; }
public string Name { get; set; }
public int ParentCategoryId { get; set; }
}
From here I would then create a new Object that self referenced itself. (You could use the existing object with the ParentCategory having the [Result] attribute on it.)
public class Category {
public int Id { get; set; }
public string Name { get; set; }
public Category ParentCategory { get; set; }
}
You could then take this and convert your flat list into a nested list.
I do have code somewhere that can do this, and for which it also provides searching methods etc, but its not on this computer. I will update tomorrow with a link to the code.