Map Parent-Child in Hierarchy with Entity Framework - entity-framework

We are using Entity Framework and We have one unique requirement, and after trying many possible options, I couldn't figure out how I can do that, below is the problem summary.
I have following Entity
public class SuperParent
{
[Key]
public int SupoerParentId {get;set;}
public virtual ICollection<Parent> Intermediates {get;set;}
}
public class Parent
{
[Key]
public int ParentId {get;set;}
[ForeignKey("SuperParentId")]
public virtual SuperParent Ancestor {get;set;}
public int SuperParentId {get;set;}
public virtual ICollection<Child> Children {get;set;}
}
public class Child
{
[Key]
public int ChildId {get;set;}
[ForeignKey("ParentId")]
public virtual Parent Ancestor {get;set;}
public int ParentId {get;set;}
/// Area of guidance required here..............
/// I just want to some what denormalize table and add SuperParentId also in
/// Child and in Database. As most of time its child we query and its very
/// efficient for us to directly query based on SuperParentId, I want to do
/// something like below:
[ForeignKey("SuperParentId")]
public virtual SuperParent Ancestor {get;set;}
public int SuperParentId SuperAncestorId {get;set;}
}
We have 1:N:N relationship, and many times we just want to bypass Parent and from SuperParent to directly want to reach to Child... Currently multi level joins are having problem and our query are not efficient, we store large amount of data and each table has 20+ columns.
Questions:
Is it possible with EF? then how I can write modelBinder
OnModelCreating to support this?
Any other alternative?

If you want to have that kind of design, then by default Child will have cascade delete from Parent and SuperParent, which is not allowed in sql.
public int SuperParentId { get; set; } // -> non nullable
If a foreign key on the dependent entity is not nullable, then Code
First sets cascade delete on the relationship. Source
You can simply remove the default cascade delete from SuperParent.
modelBuilder.Entity<Child>()
.HasRequired(c => c.Ancestor)
.WithMany()
.WillCascadeOnDelete(false);
If you have Child collection on SuperParent, mention it when calling WithMany.
public DbSet<Child> Children { get; set; }
Change above WithMany with
.WithMany(sp => sp.Children)

Related

How to change foreign key suffix in Entity Framework Core?

In EF Core with a code-first approach, by default column referencing another entity has an Id suffix - for example PersonId.
Is it possible - and if so, how? - to change it to _id, so to person_id?
Create the foreign key explicitly under the name you want - in your case Parent_Id. Keep a navigation property and foreign key property.
public int Parent_ID { get; set; }
public virtual Parent Parent { get; set; }
Map the foreign key relations using .HasForeignKey(). Something similar as below
builder.HasOne(d => d.Prop)
.WithMany(p => p.NavigationProp)
.HasForeignKey(d => d.ForeignKeyProp)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_ConstraintName");
If you prefer data annotation, you could also use
[Column("Parent_ID")]
public int ParentID { get; set; }
To add to WisdomSeeker's answer, you can use a [ForeignKey] annotation to point at a shadow property for the FK.
Given a class like a Course with a Person reference for a Teacher:
public class Course
{
[Key]
public int Id {get; set;}
// other fields.
[ForeignKey("person_id")]
public virtual Person Teacher { get; set; }
}
Alternatives as above would be:
[ForeignKey("Teacher")]
public int person_id { get; set; } // Not recommended naming convention in code.
public virtual Person Teacher { get; set; }
or
[Column("person_id"), ForeignKey("Teacher")]
public int TeacherId { get; set; }
public virtual Person Teacher { get; set; }
I generally avoid adding FK fields into classes as this leads to two sources of truth for what Teacher is assigned to a course. You have course.TeacherId and course.Teacher.Id, which could differ on update prior and after a SaveChanges. Shadow properties help avoid confusion and keep data updates consistent.
Using [Column] is common in Db-First implementations where you want to use a C# naming convention for properties to use in-code, but abide by existing/desired DB naming conventions in the database. I don't generally recommend using DB naming conventions in C# classes.

EF Core, Primary Key is not auto generated for Entity which inherit from ICollection

Here is my Entity:
public class StackImage: ICollection<StackFile>
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual Guid Id { get; set; }
private IList<StackFile> StackFiles { get; set; } = new List<StackFile>();
public StackImage()
{
}
[...] // Implementation of ICollection
}
public class StackFile
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual Guid Id { get; set; }
public string Url { get; set; }
public int Position { get; set; }
public StackFile(){}
}
stackImage.Add(new StackFile(url));
stackImage= await _stackImageRepository.UpdateAsync(stackImage);
await _unitOfWork.SaveChangesAsync();
In this sample after UpdateAsync, the StackImage Id is not generated (stackImage.Id == default) but the StackFile Id is correctly generated (stackImage[0].Id == default)
Did you already noticed this problem? My guess is, EF Core see StackImage as a list and doesn't try to generate a new Guid. How to fix this issue?
EDIT:
From what I can read on the web and by responses I received, It seems not possible to do it. If someone has the solution, please let us know :)
It seems to me that you want to design a database with (at least) two tables. A table with StackImages and a table with StackFiles.
You want to design a one-to-many relation between StackImages and StackFiles: every StackImage has zero or more StackFiles, every StackFile belongs to exactly one StackImage. In a database this is implemented using a foreign key.
Hence, it is not true that a StackImage is a StackFile. However, you can say that a StackImage has some StackFiles.
Following the entity framework code first conventions your classes should be similar to:
class StackImage
{
public Guid Id {get; set;}
...
// every StackImage has zero or more StackFiles (one-to-many):
public virtual ICollection<StackFile> StackFiles {get; set;}
}
class StackFile
{
public Guid Id {get; set;}
...
// every StackFile belongs to exactly one StackImage, using foreign key:
public Guid StackImageId {get; set;}
public virtual StackImage StackImage {get; set;}
}
finally the DbContext:
class MyDbcontext : DbContext
{
public DbSet<StackImage> StackImages {get; set;}
public DbSet<StackFile> StackFiles {get; set;}
}
Note the use of virtual properties to express the relations between the tables. As the foreign key StackImageId is supposed to be a real column, it is not virtual
In entity framework the columns of a table are represented by non-virtual properties,
the virtual properties represent the relations between the tables.
Because I followed the conventions, there is no need for attributes, nor fluent API. Entity framework detects the one-to-many collection and creates the proper tables for you. Only if you want different identifiers for your tables or columns you'll need fluent API or attributes.

Entity Framework, Unneeded Foreign key

I'm using EF Code first.
I created two classes. For simplicity, imagine that I have a User table (class) and a FileAttachment table. I want to use the FileAttachment table with many other classes, so that any part of the application that requires having a FileAttachment can reuse that table. The problem is that when EF generates the schema, it creates a Foreign Key in the FileAttachment table back to User table. Is there a way to disable that?
Thanks
You need to build an intermediate class.
public class UserDocument
{
public int Id {get;set;}
public int UserId {get;set;}
public virtual User User {get;set;}
public int FileAttachmentId {get;set;}
public virtual FileAttachment FileAttachment {get;set;}
}
So your user class can now have:
public virtual ICollection<UserDocument> Documents {get;set;}
And in this case, FileAttachment class will not have reference to User.
If you want now to build some other document type, just implement another intermediate type, i.e. imagine you want to have CustomerDocument:
public class CustomerDocument
{
public int Id {get;set;}
public int CustomerId {get;set;}
public virtual Customer Customer {get;set;}
public int FileAttachmentId {get;set;}
public virtual FileAttachment FileAttachment {get;set;}
}
And then your hypothetical Customer class would have:
public virtual ICollection<CustomerDocument> Documents {get;set;}

EF Using FK's for 1:1 Relationships

when working with my POCO objects, I prefer to delay instantiation of navigation properties as much as possible (performance reasons). Instead I'd like to use the FK Value on my entities.
For Example:
class Car
{
public int Id {get;set;}
public int RegistrationId {get;set;}
public virtual Registration Registrations {get;set;} // Nav Property
}
class Registration
{
public int Id {get;set;}
public int CarId {get;set;}
public virtual Car Car {get;set;} // Nav Property
}
Given this is a 1:1 Relationship, how can I get EF to populate my FK Values for me? (using EF 6.0.1)

Provide Entity Framework hints about how / what order to cascade delete

I am doing a Code-first EF approach except for the fact that I am managing the database (for schema change issues that I believe still exist in code-first). I have the following tables / model objects (eliding several parameters, etc) :
class A {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual List<B> BList {get;set;}
public virtual List<C> CList {get;set;}
}
class B {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("A"), Required]
public int AId {get; set;}
public virtual A parent {get;set;}
public virtual List<D> DList {get;set;}
}
class C {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("A"), Required]
public int AId {get;set;}
[ForeignKey("someD"), Required]
public int someDId {get;set}
public virtual D someD
}
class D {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("B"), Required]
public int BId {get;set;}
public string data {get;set;}
}
There's more to it than that, but the idea is that from a parent object A, I can navigate to a D along two different relationships. The Cascade delete relationships in the database are set up such that A->B, A->C, and B->D are cascades; C->D is 'no action'.
Therefore, when I go to delete an A (DbSet.Remove(a); SaveChanges(); etc ) EF needs to delete the C branch first, so that it can later delete the D's and not violate the FK constraint C->D. However, it appears to be choosing the other path, as the delete throws an exception about said constraint being violated. Is there a way for me to provide a hint (attribute or otherwise) to EF to tell it something else about these relationships so it can delete things in the right order?
edit Added more clarity to the approach in the code; Also, this thought occurs to me: I'm not explicitly capturing the relationship from C->D on D's end - that is, there is no navigation property on D that points to C; because C is a part of a Table-per-type scheme and not all D's point to a good collection of C's base class. Do I need to have some kind of navigation property in D back to C for EF to understand what I want?