Entity Framework Core multiple table relationship - asp.net

I am trying to create relationship where parent has many children and also includes the oldest child. How to correctly configure that relationship and how to insert the data into the tables?
public class Parent
{
public int Id { get; set; }
public int Name { get; set; }
public int ChildId { get; set; }
public virtual Child Child { get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
public int Age { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}

I would recommend against trying to store it as a separate property. Instead, calculate it from existing data with a get-only property something like:
public Child OldestChild => Childs.OrderByDescending(c => c.Age).FirstOrDefault();
If you are concerned about the cost of repeated access, you can look into wrapping the access logic using the Lazy<> class or some other caching methods. If you do, be aware that changes to the Childs collection would then not be reflected in the cached OldestChild result.
FOLLOW-UP: The above is still incomplete as it doesn't resolve cases of children of the same age - close birthdates, twins, adoptees, etc. That also raises the question: Why are are you modeling age (which changes over time vs Birthdate, which is fixed.

Related

Tree structure with reference to root in Entity Framework

I'm trying to model a tree structure for orders in Entity Framework. Right now I've go the following:
public class ProjectModel
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int CustomerId { get; set; }
public virtual List<ProjectNode> Nodes { get; set; }
}
public class ProjectNode
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Path { get; set; }
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public virtual List<ProjectNode> Children { get; set; }
}
What I need to be able to do is get a reference to the root ProjectModel at any level of ProjectNode in order to authorize a given user actually having permission to view and change the project which contains the ProjectNode.
public class ProjectNode {
public int ProjectId { get; set; } //<-- this
...
public class ProjectModel {
[Key]
public int Id { get; set; } //<-- containing the value of this
}
My question is whether its possible to have a theoretical ProjectId property populated at every level of the tree structure, or if I need to set it manually.
I had something working that at first blush appeared to allow this functionality, but upon further investigation only populated the ProjectId for ProjectNodes contained in the ProjectModel's Nodes property.
It seems to me like it would be super inefficient to recurse backwards through the structure to get to the root.
Credit due to #TestWell for this answer -
Apparently, all I needed to do for EF to automatically populate the ProjectId property on the ProjectNode was to change the name of the Id property in ProjectModel to ProjectId.
Unfortunately, this doesn't appear to work if I add a CustomerId property to the ProjectNode that I would like automatically populated from the property of the same name on the node's root ProjectModel, which I realized is the more efficient solution to what I'm trying to do.

Load all hierarchical references with Servicestack ORMLite

Is there any way to preload all nested and sub-nested references with servicestack / ormlite?
public class Person
{
public int Id { get; set; }
[References(typeof(Pants))]
public int PantsId { get; set; }
[Reference]
public Pants Pants { get; set; }
}
public class Pants
{
public int Id { get; set; }
[References(typeof(Pocket))]
public int PocketId { get; set; }
[Reference]
public Pocket Pocket { get; set; }
}
public class Pocket
{
public int Id { get; set; }
public int Depth { get; set; }
}
Db.LoadSelect<Person>()
When I load a person using Db.LoadSelect() it only fetches references up to person.Pants... person.Pants.Pocket is null. I would have to do a Db.LoadReferences(person.Pants) for it to load the pocket reference.
Any way of automatically loading all the nested references, or is it limited to one layer?
Thanks.
OrmLite's db.Load* API's is limited to loading 1-level depth of references. The Db.LoadReferences(instance) can be used to further fetch the disconnected POCO's references.
You should also be mindful if loading references individually to avoid N+1 queries by loading them in a loop, i.e. when possible it's better to use a single query to fetch related records to avoid multiple db hits.

Asp.net child object with two parents

Let s say i have two tables, "article" and "event". They both have comments so they both need access to the "comments" entity.
How do i declare this relationship? having two foreign keys in "comments" is problematic since one of the foreign keys will be null in every case.
Can i just not declare any foreign key? Is there a convention on how to deal with this situation?
public class Article
{
public int Id { get; set; }
public virtual IEnumerable<Comment> comments { get; set; }
}
public class Event
{
public int Id { get; set; }
public virtual IEnumerable<Comment> comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public Article article { get; set; }
public Event event{ get; set; }
}
I would maybe consider a dedicated comment table per entity. One comment table for article and another for event. Especially if individual comments never apply to both articles and events.
However, there is nothing that prevents you from making a shared one with two FKs as you described.

Is it OK to declare a DBSet in the context for both a base table and a derived table?

I have a SalesOrder table which inherits from a SalesDocument table using Table Per Type Inheritance
The ( simplified) table classes are;
[Table("SalesDocumentHeaders")]
public abstract class SalesDocumentHeader
{
[ForeignKey("CreatedByUserId")]
public virtual User CreatedBy { get; set; }
[Required]
public int CreatedByUserId { get; set; }
[Required]
public virtual DateTime? DocumentDate { get; set; }
[Required]
public String ReferenceNumber { get; set; }
}
[Table("SalesOrders")]
public class SalesOrder : SalesDocumentHeader
{
[Required]
public String CustomerOrderNumber { get; set; }
public DateTime? DeliverBy { get; set; }
public virtual SortableBindingList<SalesOrderLine> Lines { get; set; }
}
The context contains
public DbSet<SalesOrder> SalesOrders { get; set; }
public DbSet<SalesDocumentHeader> SalesDocumentHeaders { get; set; }
It doesn't strictly need the SalesOrders DBSet, since SalesOrder inherits from SalesDocumentHeader however I find it convenient.
It seems to work OK, but I am worried that there are 2 ways of reaching the same record , am I doing something wrong?
Usually you only need to keep the DBSet for the base table. This helps when you have multiple derived tables (call them A and B) and you need to decide the actual type dynamically.
For example if you have another entity which references type A or B (like a user can have different types of contact information), you can reference the base table and EF will resolve the correct concrete type at runtime. Though of course this adds some extra casting code.

make both models aware of each other with a one-to-one relationship in asp.net entity framework

I am unable to get the one to one relationship with EF to work properly. I've scored blogs, SO, and msdn docs, but nothing I do seems to work.
I have two models, a Class and an Exam that look like the following:
[Table("classes")]
public class Class
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(255), Required]
public string Title { get; set; }
public virtual Exam Exam { get; set; }
}
[Table("exams")]
public class Exam
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:h:mm tt}")]
public DateTime? Time { get; set; }
public int ClassId { get; set; }
public virtual Class Class { get; set; }
}
I want to be able to access the exam from the Class and the Class from the Exam, but no matter what I do, I find some error.
Trying to create/run migrations I get the following.
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
If I add this to my context:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<Exam>()
.HasRequired(e => e.Class)
.WithOptional(c => c.Exam);
}
I get the following error:
System.Data.Entity.Infrastructure.DbUpdateException: Entities in 'BenchContext.Exams' participate in the 'Class_Exam' relationship. 0 related 'Class_Exam_Source' were found. 1 'Class_Exam_Source' is expected. ---> System.Data.UpdateException: Entities in 'BenchContext.Exams' participate in the 'Class_Exam' relationship. 0 related 'Class_Exam_Source' were found. 1 'Class_Exam_Source' is expected.
I'm not sure how to tell the fluent api how to correctly foreign key between the two models and nothing I do seems to affect it.
What am I missing?
You are trying to build one-to-one relation through ClassId property in Exam class. That requires ClassId to be unique (= unique constraint) but unique constraint are not supported by EF yet.
The only way EF currently supports real one-to-one relation is by sharing primary key:
[Table("classes")]
public class Class
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(255), Required]
public string Title { get; set; }
public virtual Exam Exam { get; set; }
}
[Table("exams")]
public class Exam
{
[Key, ForeignKey("Class")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:h:mm tt}")]
public DateTime? Time { get; set; }
public virtual Class Class { get; set; }
}
The Id in Exams table is both PK and FK to Classes table. It cannot have autogenerated value. While this is one-to-one relation it still has 1 - 0..1 multiplicity (Class can exist without Exam but Exam must have Class).

Resources