Linq retrive the parent object - c#

Im using entityframework and the parent object MyObject already exist in my context (and childs loaded).
public class MyObject
{
public int id { get; set; }
public string name { get; set; }
public IEnumerable<Child> child { get; set; }
}
public class Child
{
public int id { get; set; }
public string name { get; set; }
public IEnumerable<Child2> child2 { get; set; }
}
public class Child2
{
public int id { get; set; }
public string name { get; set; }
}
I want to make a condition on a Child2 property with keeping an instance of the Parent object.
var myFiletredMyObject = myObject.child.Where(c => c.child2.Any(c2 => c2.id == 1));
This gives me back a collection of Child object, how can I do to get the parent : MyObject ?

If you've followed the entity framework code first conventions, then you should have written the relations between your tables as virtual properties.
In entity framework, the columns of your tables are represented by non-virtual properties; the virtual properties represent the relations between your tables (one-to-many, many-to-many).
Furthermore: it is better to describe the one-to-many as an ICollection<...> instead of an IEnumerable<...>. This enables you to ask for the number of children, and to Add a Child, something that entity framework can translate into SQL easily.
So with a little change in your code, changes that will give you the same database (well, I've renamed the properties a little, to make it easier in my example)
public class Parent
{
public int id { get; set; }
public string name { get; set; }
// every Parent has zero or more Children (one-to-many)
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int id { get; set; }
public string name { get; set; }
// every Child is the child of exactly one Parent, using a foreign key
public int ParentId {get; set;}
public virtual Parent Parent {get; set;}
// every Child has zero or more Child2 (one-to-many)
public virtual ICollection<Child2> Children2 { get; set; }
}
public class Child2
{
public int id { get; set; }
public string name { get; set; }
// every Child2 belongs to exactly one Child, using foreign key
public int ChildId {get; set;}
public virtual Child Child {get; set;}
}
And the DbContext:
public class FamilyDbContext
{
public DbSet<Parent> Parents {get; set;}
public DbSet<Child> Children {get; set;}
public DbSet<Child2> Children2 {get; set;}
}
This is enough for entity framework to detect your tables, the columns in the tables and the relations between the tables (in this case: one-to-many).
Because of the odd plural of Child you might get an odd table name: Childs. To prevent this, you might want to use fluent API. Add to your DbContext:
public override void OnModelCreating(DbModelBuilder modelBuilder);
{
// configure the one-to-many between Parent and Child using the foreign key:
modelBuilder.Entity<Parent>()
.HasMany(parent => parent.Children)
.HasRequired(child => child.Parent)
.HasForeignKey(child => child.ParentId);
// configure the one-to-many between Child and Child2 using foreign key
// make sure that the table gets a proper name
modelBuilder.Entity<Child>().ToTable("Children")
.HasMany(child => child.Children2)
.HasRequired(child2 => child2.Child)
.HasForeignKey(child2 => child2.ChildId);
// the one-to-many relations are configured. Set the last table name
modelBuilder.Entity<Child2>().ToTable("Children2");
}
Back to your question
If you've configured your classes the way that entity framework was meant, your query will be easy:
var myFilteredChildrenWithTheirParents = dbContext.Children
.Where(child => ...)
.Select(child => new
{
// select only the Child properties you really plan to use:
Id = child.Id,
Name = child.Name,
Parent = new
{ // again: select only the Parent properties you plan to use:
Id = parent.Id,
Name = parent.Name,
},
// no need for the foreign key, you already selected the value in Parent
// ParentId = child.ParentId,
});

Considering EntityFramework will return list of models from database you can filter your EF object like this.
var filterResult = myObject.Where(x => x.child.Any(a=>a.child2.Any(c2 => c2.id == 1))).ToList();
Filter Result will be of type MyObject.
if your Object of type MyObject you can use ternary operator to apply criteria.
var filterResult = myObject.child.Where(a => a.child2.Any(c2 => c2.id == 1)).ToList().Count > 0 ? myObject : null;

Related

Proxy Items in Entity Framwork Core

Is it possible to create as a proxy element in EF Core?
For example, in the database there is the element with the id 1, which has the name Example. The second element with id 2 has no name (is null), but has a reference to element 1 ("id_replace"). In this case I would like the name returned by item 2 to be "Example" like item 1. And also the "Includes" quote to item 1 references.
The reason I have such a strange idea is that I need to have linked the elements, and if element 1 changes, the changes made are displayed on element 2 as well.
Example Registers in Database
Sure you can. Assuming that your class is:
public class YourClass
{
public int id { get; set; }
public string name { get; set; }
public int? id_replace { get; set; }
}
In your class, you need to have the one to many referencing properties:
public YourClass parent { get; set; }
public IList<YourClass> children { get; set; }
Then, in your DbContext class, in the override OnModelCreating function, you need to have a relationship set in the fluent API that indicates that id_replace is a self-referencing foreign key:
modelBuilder.Entity<YourClass>(entity =>
{
entity.HasOne(x => x.parent)
.WithMany(x => x.children)
.HasForeignKey(x => x.id_replace)
.OnDelete(DeleteBehavior.SetNull);
});
After doing that(and migrating), you have the necessary navigation properties to be able to add computed properties that do not represent anything in the database. So your class can have the property:
public int alt_name => name??$"\"{parent.name}\"";
So eventually, your class will look something like this:
public class YourClass
{
public int id { get; set; }
public string name { get; set; }
public int? id_replace { get; set; }
public YourClass parent { get; set; }
public IList<YourClass> children { get; set; }
public int alt_name => name??$"\"{parent.name}\"";
}
That way, you can discard the name property and just call on the alt_name property. You can even set the name property as private or change the names to avoid confusion.

How to represent a relationship between a parent and multiple child properties of the same type?

Consider the following two entities*:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Child PrimaryChild { get; set; }
public virtual Child SecondaryChild { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
// Foreign keys to be added here or
// in the other class (that's what I'm trying to figure out)
}
As the code shows, a parent needs to have a primary child and a secondary child; and (preferably) when the parent is deleted, both children should be deleted as well.
Is it even possible to represent such relationship with EF?
If the parent were to have only one child, I could easily use a one-to-one relationship (1 to 0..1) by turning the primary key of Child into both PK and FK. Obviously, that doesn't work with the current situation because, for one, a primary key cannot have multiple values.
This feels like it's a common enough situation that it should have a well-known solution. However, after checking countless posts, I still can't find one. The closest thing I could find is this answer but it doesn't work because:
It creates * to 0..1 relationships.
When I ignored that fact and tried to execute the following code:
var c1 = new Child { Name = "C1" };
var c2 = new Child { Name = "C2" };
context.Parents.Add(new Parent { Name = "P1", PrimaryChild = c1, SecondaryChild = c2 });
...it threw a DbUpdateException with the message:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
How can this problem be solved?
* This is a simplified example. In the real life scenario, there are relationships between the parent and other child entities with two or more references to each.
Try this method,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
// this class will represent a person, a parent or a child since they have the same attributes
class Person
{
protected int _id;
protected string _name;
public int Id { get => _id; }
public string Name { get => _name; }
// Any object wether it's a parent or a child must be instantiated with those properties
public Person(int id, string name)
{
this._id = id;
this._name = name;
}
}
// this represents a parent which is a persone but have two persons reprenseting it's children. the children can't be instanciated alone.
class Parent : Person
{
private Person _primaryChild;
private Person _secondaryChild;
public Person PrimaryChild { get => _primaryChild; }
public Person SecondaryChild { get => _secondaryChild; }
// this creates the parent with it's children. one the parent dispose its children will be deleted.
public Parent(Person parent, Person primaryChild, Person secondaryChild) : base( parent.Id, parent.Name)
{
// this primaryChild enforce that a parent must have two different children to represents a primary and a secondry child.
if(primaryChild.Id != secondaryChild.Id)
{
this._id = parent.Id;
this._name = parent.Name;
this._primaryChild = primaryChild;
this._secondaryChild = secondaryChild;
}
else
{
throw new Exception("Children must not be tweens.");
}
}
}
class Program
{
static void Main(string[] args)
{
// instanciating a person does'nt represent an actor in the process. A parent must be instianciated.
try
{
// creating a new parent with its related children
var parent = new Parent(new Person(1, "Parent"), new Person(1, "ChildOne"), new Person(2, "ChildTwo"));
// diplaying children names
Console.WriteLine($"Primary Child is {parent.PrimaryChild.Name}. Secondary Child is {parent.SecondaryChild.Name}.");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
The child class should contain a foreign key for the parent class, in this instance I've used a composite key for the one to one relationship.
public class Child
{
[Key]
[Column(Order = 1)]
public int Id { get; set; }
public string Name { get; set; }
[Key, ForeignKey("Parent")]
[Column(Order = 2)]
public int ParentId { get; set; }
}
Then in the OnModelCreating of your context you will want to set up the constraints.
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Parent>()
.HasRequired(e => e.PrimaryChild)
.WithRequiredPrincipal(e => e.Parent);
modelBuilder.Entity<Parent>()
.HasRequired(e => e.SecondaryChild)
.WithRequiredPrincipal(e => e.Parent);
modelBuilder.Entity<Child>()
.HasKey(e => e.ParentId);
}
You can extend this as you need.
There are two ways to create such relationships:
Parent has two foreign keys to the respective children, close to what you have done.
Children have foreign keys to the parent, which would be a simple one to many.
The first option very easily restricts a parent to two children and makes it very easy to distinguish between the primary and secondary child.
The second option, you would need to add a type to the child objects if you have the need to distinguish primary from secondary. Using the parent's Id and the type as a unique index would allow you to limit the children to a single primary and a single secondary child per parent.
Now as far as when the parent is deleted, if you have cascading deletes for the relationship it will happen automatically. I know that in second option, the simple one-to-many, that cascading deletes will work as expected. The first option, basically two one-to-one relationships, you can set up cascading deletes, but I would suspect that you'll need to ensure it is a one-way cascade. e.g. if the parent is deleted the children are too, but if a child is deleted the parent should remain.
I would lean towards the children having the foreign keys and a type. So something along these lines:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
[Index("IX_Child_Parent_Type", 1, IsUnique = true)]
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
[Index("IX_Child_Parent_Type", 2, IsUnique = true)]
public int ChildTypeId { get; set; }
public virtual ChildType Type { get; set; }
}
public class ChildType
{
public int Id { get; set; }
public string Name { get; set; }
}
The other option would look like this:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Child PrimaryChild { get; set; }
public virtual Child SecondaryChild { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
}
I came up with a hacky solution but I'm still hoping for a better one.
Instead of one-to-one relationships, I created a good old one-to-many relationship (Parent has many of Child) with a property in the Child class to store the type (primary/secondary), and created unmapped properties in the Parent class to replace the original navigation properties.
Here's what the code would look like for the example the question:
Child class:
public enum ChildType { Primary, Secondary }
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public ChildType ChildType { get; set; }
[ForeignKey("Parent")]
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Parent class:
public class Parent
{
public Parent()
{
Children = new HashSet<Child>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Child> Children { get; set; }
[NotMapped]
public Child PrimaryChild
{
get
{
return Children.SingleOrDefault(c => c.ChildType == ChildType.Primary);
}
set
{
var existingChild =
Children.SingleOrDefault(c => c.ChildType == ChildType.Primary);
if (existingChild != null) Children.Remove(existingChild);
Children.Add(value);
}
}
[NotMapped]
public Child SecondaryChild
{
get
{
return Children.SingleOrDefault(c => c.ChildType == ChildType.Secondary);
}
set
{
var existingChild =
Children.SingleOrDefault(c => c.ChildType == ChildType.Secondary);
if (existingChild != null) Children.Remove(existingChild);
Children.Add(value);
}
}
}
Usage:
var c1 = new Child { Name = "C1", ChildType = ChildType.Primary };
var c2 = new Child { Name = "C2", ChildType = ChildType.Secondary };
context.Parents.Add(new Parent { Name = "P1", PrimaryChild = c1, SecondaryChild = c2 });
Obviously, this doesn't enforce the minimum or maximum number of children that the parent can have or what ChildType they must be. It's the best I could come up with though.
This code was tested in Visual Studio and works properly
var c1 = new Child { Name = "C1" };
var c2 = new Child { Name = "C2" };
_context.Parents.Add(new Parent { Name = "P1", PrimaryChild = c1, SecondaryChild = c2 });
var parents = _context.Parents
.Include(i => i.PrimaryChild)
.Include(i => i.SecondaryChild)
.ToList();
To try it you have to add relations to your classes
public class Parent
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int? PrimaryChildId { get; set; }
public int? SecondaryChildId { get; set; }
[ForeignKey(nameof(PrimaryChildId))]
[InverseProperty("PrimaryChildParents")]
public virtual Child PrimaryChild { get; set; }
[ForeignKey(nameof(SecondaryChildId))]
[InverseProperty("SecondaryChildParents")]
public virtual Child SecondaryChild { get; set; }
}
public class Child
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty(nameof(Parent.PrimaryChild))]
public virtual ICollection<Parent> PrimaryChildParents { get; set; }
[InverseProperty(nameof(Parent.SecondaryChild))]
public virtual ICollection<Parent> SecondaryChildParents { get; set; }
}
if you want to make one-to-one and since you are using EF6
you can add some constraints to the foreign keys
[Index("IX_Parents_PrimaryChildId", 1, IsUnique = true)]
public int? PrimaryChildId { get; set; }
[Index("IX_Parents_SecondaryChildId", 2, IsUnique = true)]
public int? SecondaryChildId { get; set; }
Make some small mods to the entities and use fluent api to specify a one to many relationship
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsPrimary {get; set;}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasKey(p => p.Id)
.WithMany(p => p.Child)
.HasForeignKey(s => s.ParentId);
}
public DbSet<Child> Children{ get; set; }

Map list of children to parent object from join table with LINQ

I've searched and can't find a similar question where three tables come into play. I have three tables: Parent, Child, and ParentChild. ParentChild is a many-to-many join table that lets you set multiple children to multiple parents. I'm using .Net Core 5.
Here are the dto classes for the tables:
public class Parent {
public int Id { get; set; }
public int UserId { get; set; }
public List<Child> Children { get; set; }
}
public class Child {
public int Id { get; set; }
public string Name { get; set; }
}
public class ParentChild {
public int Id { get; set; }
public int ParentId { get; set; }
public int ChildId { get; set; }
}
Here's my linq statement:
var parentChildGroup = from parent in parentList
join parentChild in parentChildList
on parent.Id equals parentChild.ParentId
join child in childList on parentChild.ChildId equals child.Id
group child by child.Id into newGroup
select new {
Children = newGroup,
};
I've gone through several different iterations based on examples I've found online and I've created a .Net fiddle with the full code:
https://dotnetfiddle.net/Hh12V3
What I want is a list of all Parent objects with a child list of all Child objects.
So, I should be able to iterate through Parent.Children for example. I just can't figure out how to insert the group of Child objects into the Children property of the Parent. It's probably simple and I've made this overly complicated. Any help would be appreciated.
Thanks!
Change the model of the parent to contains list of children
public class Parent {
public int Id { get; set; }
public int UserId { get; set; }
public virtual List<Child> Children {get;set;} // <<<<<<<<<<<<
}
then you can select it as following
var parentChildGroup =parentList.Select(p=>
{
p.Children = parentChildList.Where(pc=>pc.ParentId == p.Id)
.SelectMany(pc=>childList.Where(c=>c.Id == pc.ChildId))
.ToList();
return p;
});
if you use net5 this should be enough
var parentChildGroup = db.Parents.Include(c=> c.Children).ToList();

saving entities that do not expose foreign key for their relationship

I am little bit new in EF code-first. Let's say I've two entities as follows-
//Parent may have many childs
class Parent
{
[Key]
public int ParentId { get; set; }
public string ParentName {get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
//Child will have one parent.
class Child
{
[Key]
public int ChildId { get; set; }
public string ChildName {get; set; }
public int ParentId {get; set; }
public virtual Parent Parent{ get; set; }
}
I am trying to save as -
var parent= db.Parents.find(1);
var child=new Child();
....
child.Parent= parent
db.Childs.Add(child);
db.SaveChanges();
But it gives me the following exception-
an error occurred while saving entities that do not expose foreign key for their relationship.
Perhaps, I'm missing something here. Any help?
Did you define the relation in fluent api? If not you must either define it via attributes or via fluent api. Like this:
//Child will have one parent.
class Child
{
[Key]
public int ChildId { get; set; }
public string ParenChildName {get; set; }
public int ParentId {get; set; }
[ForeignKey("ParentId")]
public virtual Parent Parent{ get; set; }
}
Also setting the Parent property of child will not help, since the parent is defined by the ParentId property, you must set ParentId=parent.ParentId

Entity Framework: self referencing tree with different ParrentID

I have class like this:
class Tree
{
[Key]
public int TreeID { get; set; }
public int? ParentID { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public virtual Tree Parrent { get; set; }
public virtual ICollection<Tree> Children { get; set; }
}
And configuration class :
class TreeConfiguration : EntityTypeConfiguration<Tree>
{
public TreeConfiguration()
{
this.HasOptional(d => d.Parrent)
.WithMany(p => p.Children)
.HasForeignKey( d => d.ParentID)
.WillCascadeOnDelete(false);
}
}
It works good, but what I want is to child node use ID (from parrent node) for ParentID, not TreeID.
It should work like this:
Each node has an id - that is ID - and id of its parent - that is ParentID. TreeID is primary key and it has nothing to do with child-parent mapping - it's for database only.
I can't change the columns so I it must be this way
Well.. apparently it has something to do with the fact that FKs must always point to primary keys. So it's not possible.
Why don't you use your "ID" as a unique column - e.g. use a unique index for it?
Then you have the same semantics like you would have if ID would be the PK..

Categories

Resources