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

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 ?

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

Querying a table that relates to multiple entity types

So currently I have an application model of:
Note -> Thing
(A note can relate to many things)
A thing can be numerous entities (for this example lets use Computer & Car)
e.g
Note -> Computer
-> Car
So right now, I have the schema of
Note -> ComputerNote -> Computer
Note -> CarNote -> Car
The problem is that because the entity links are in separate tables, it requires a new query to be written rather than just using filtering in the WHERE clause.
Ideally it would be nice to have an EntityId & EntityTypeId column & on the Note table that would hold the primary key of the related entity and the type of the entity. Thus application logic could look for all Car notes where the car is x without a separate query for each type, but.. this would mean I lose referential integrity. Is there a better way, or is what I have suggested an acceptable design?
Entity Framework Model's:
public partial class Note
{
public Note()
{
NoteComputer = new HashSet<NoteComputer>();
NoteCar = new HashSet<NoteCar>();
NoteThing = new HashSet<NoteThing>();
}
public int Id { get; set; }
public string Value { get; set; }
public string CreatedByUserId { get; set; }
public DateTime CreatedDateTime { get; set; }
public ICollection<NoteComputer> NoteComputer { get; set; }
public ICollection<NoteCar> NoteCar { get; set; }
public ICollection<NoteThing> NoteThing { get; set; }
}
public partial class NoteCar
{
public int Id { get; set; }
public int NoteId { get; set; }
public int CarId { get; set; }
public Car Car { get; set; }
public Note Note { get; set; }
}
public partial class NoteComputer
{
public int Id { get; set; }
public int NoteId { get; set; }
public int ComputerId { get; set; }
public Computer Computer { get; set; }
public Note Note { get; set; }
}
public partial class NoteThing
{
public int Id { get; set; }
public int NoteId { get; set; }
public int ThingId { get; set; }
public Thing Thing { get; set; }
public Note Note { get; set; }
}
As there seems to be no nice way to handle this at the database level, I've found it best to handle this at the application level using the concrete type database schema, to produce dynamic joins.
Example in Entity Framework Core:
public GenericEntityProvider
{
private readonly IEnumerable<IEntityProvider> _entityProviders;
private readonly DatabaseContext _context;
public GenericEntityProvider(IEnumerable<IEntityProvider> entityProviders, DatabaseContext context)
{
_entityProviders = entityProviders;
_context = context;
}
public IEnumerable<Note> Get(Type type, int id) {
var provider = _entityProviders.GetPredicate(type, id);
return _context.Notes.Where(provider);
}
}
public CarNoteProvider : IEntityProvider
{
public Expression<Func<Note, bool>> GetPredicate(Type type, int id)
{
return x => x.CarNote.Any(cn => cn.CarId == id);
}
}

EF Core Join using Include but ForeignKey is not Primary Key on the other table

I am trying to relate my Tables with ForeignKey and PrimaryKey on the other end. But now i will be using a ForeignKey which is not the primary for the said table. I was using [InverseProperty] but i think there's a bug with it since i've been looking around for hours already and all of them says the same thing about it.
Documents Table:
public class Document
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DocumentId { get; set; }
public int ProjectId { get; set; }
public int DepartmentId { get; set; }
public int AuthorId { get; set; }
[NotMapped]
public virtual User Author { get; set; }
}
Users
public class User
{
[Key]
public int UserId { get; set; }
public int AuthUserId { get; set; }
public string DisplayName { get; set; }
[NotMapped]
[ForeignKey("AuthorId")]
public virtual Document Document { get; set; }
}
Context:
modelBuilder.Entity<User>(entity =>
{
entity.HasOne(u => u.Document)
.WithMany("AuthorId");
});
I am trying to use the solution they here, but no luck.
Any help would really be appreciated. Thanks!
But now i will be using a ForeignKey which is not the primary for the said table.
To do this you can use EF Core Alternate Keys feature. But first correct your model class set up as follows: (As you said a User will have multiple Document)
public class Document
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DocumentId { get; set; }
public int ProjectId { get; set; }
public int DepartmentId { get; set; }
public int AuthorId { get; set; }
public User Author { get; set; }
}
public class User
{
[Key]
public int UserId { get; set; }
public int AuthUserId { get; set; }
public string DisplayName { get; set; }
public ICollection<Document> Documents { get; set; }
}
Then in the Fluent API configuration as follows:
modelBuilder.Entity<Document>()
.HasOne(p => p.Author)
.WithMany(b => b.Documents)
.HasForeignKey(p => p.AuthorId)
.HasPrincipalKey(b => b.AuthUserId); // <-- here you are specifying `AuthUserId` as `PrincipalKey` in the relation which is not primary key

saving reference using ServiceStack ORMLite

I am using ORMLite as my ORM and I am using it with following structure which contains the foreign key relation ship:
public class Order
{
[AutoIncrement]
public int Id { get; set; }
[Reference]
public Item Item { get; set; }
public string ProUserId { get; set; }
public string Details { get; set; }
}
public class Item
{
[AutoIncrement]
public int Id { get; set; }
public string Description { get; set; }
}
As we can see that Order contains the reference to the Item. In DB Order table has a foreign key called ItemId in the table and I have annotated that key in the design view with [Reference] attribute.
I am trying to save the Order with following code:
var order = new Order
{
Item = new Item
{
Id = 3,
Description = "Something"
},
ProUserId = "kunal#kunal.com",
Details = "fdfsdfsd"
};
Db.Save(order,references:true);
I was hoping that ORMLite would pick up the relationship and with ItemID in the Order table but it did not and it did throw following error instead:
Cannot insert the value NULL into column 'ItemId', table 'WebApp.dbo.Order'; column does not allow nulls. INSERT fails.
I tried changing my schema and addred OrderId column in my Item table with reference there and that works fine. But that is not the correct design. Should I make any changes in my code/schema to support this feature?
You still need to provide the foreign key that OrmLite can use to store the relationship, e.g. either on the Child/ForeignKey table:
public class Order
{
[AutoIncrement]
public int Id { get; set; }
[Reference]
public Item Item { get; set; }
public string ProUserId { get; set; }
public string Details { get; set; }
}
public class Item
{
[AutoIncrement]
public int Id { get; set; }
public int OrderId { get; set; } //Parent Table PK
public string Description { get; set; }
}
Or for 1:1 relationships, can be on the Parent table, e.g:
public class Order
{
[AutoIncrement]
public int Id { get; set; }
[Reference]
public Item Item { get; set; }
public int ItemId { get; set; } //Child Table PK
public string ProUserId { get; set; }
public string Details { get; set; }
}
public class Item
{
[AutoIncrement]
public int Id { get; set; }
public string Description { get; set; }
}

Entity Framework - Relationship confusion

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.