I am in need of a solution to retrieve a parent object from a child using EF lazy-loading. I have 10 unrelated classes that can use the one type of child and as such do not know how to setup a LINQ query for it. I have looked here but this makes me think I have to know that the child is in fact tied to Parent1. In the link, I know a blog has a post, but a post won't have a blog. I do not have this type of solid relationship.
Here is an example of my class setup:
public class Parent1
{
public int Parent1Id { get; set; }
... other properties ...
public virtual Child Child { get; set; }
}
public class Parent2
{
public int Parent2Id { get; set; }
... other properties ...
public virtual Child Child { get; set; }
}
public class Parent3
{
public int Parent3Id { get; set; }
... other properties ...
public virtual Child Child { get; set; }
}
public class Child
{
public int ChildId { get; set; }
... other properties ...
}
The child has been created, added to Parent2 and saved to the database via the DbContext.
After this I retrieve the child object and want to determine which parent class it is associated with.
EDIT
I have a generic call like this
public T GetById<T>(int id) where T : Base, new()
{
T entity = _Context.Set<T>().Find(id);
return entity;
}
Then I make the call like this
Child childObj = DB.GetByID<Child>(id);
Related
I have two entities say Parent, and Child; each parent can have at most two child references. I have set up my entities as follows:
class Parent
{
[Key]
public int ParentId { get; set; }
public int PrimaryChildId{ get; set; }
public Child PrimaryChild { get; set; }
public int SecondaryChildId { get; set; }
public Child? SecondaryChild { get; set; }
// remaining properties
}
class Child
{
[Key]
public int ChildId { get; set; }
public int ParentId { get; set; }
public Parent Parent {get; set; }
// remaining child properties
}
In the DbContext.OnModelCreating I have this code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>(builder =>
{
builder.HasOne(p => p.PrimaryChild);
builder.HasOne(p => p.SecondaryChild);
});
}
This isn't enough to accomplish what I'm trying to achieve here. I get an error:
Unable to determine the relationship represented by navigation property 'Child.Parent' of type 'Parent'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'
I've tried to set up the relationship from the Child entity, but I get different errors because this makes me set up two relationships for the same property. I don't want to have two navigation properties on my child when I know only one will be used at a time as it would make for a confusing model.
I've searched the internet a bit, but I'm not having any luck finding relationships that are set up in this manner.
I've been trying something like this for the last few days, and after trying all sorts of Data Annotations and Fluent API nonsense, the cleanest solution I could come up with turned out to be very simple which requires neither. It only requires adding a 'private' constructor to the Parent class (or a 'protected' one if you're using Lazy Loading) into which your 'DbContext' object is injected. Just set up your 'Parent' and 'Child' classes as a normal one-to-many relationship, and with your database context now available from within the 'Parent' entity, you can make 'PrimaryChild' and 'SecondaryChild' simply return a query from the database using the Find() method. The Find() method also makes use of caching, so if you call the getter more than once, it will only make one trip to the database.
Here is the documentation on this ability: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#injecting-services
Note: the 'PrimaryChild' and 'SecondaryChild' properties are read-only. To modify them, set the 'PrimaryChildId' and 'SecondaryChildId' properties.
class Parent
{
public Parent() { }
private MyDbContext Context { get; set; }
// make the following constructor 'protected' if you're using Lazy Loading
private Parent(MyDbContext Context) { this.Context = Context; }
[Key]
public int ParentId { get; set; }
public int PrimaryChildId { get; set; }
public Child PrimaryChild { get { return Context.Children.Find(PrimaryChildId); } }
public int? SecondaryChildId { get; set; }
public Child SecondaryChild { get { return Context.Children.Find(SecondaryChildId); } }
// remaining properties
}
class Child
{
[Key]
public int ChildId { get; set; }
public int ParentId { get; set; }
public Parent Parent { get; set; }
// remaining child properties
}
I use Entity Framework 6 (Code First). I have a class:
public class DialogSession {...}
And another class with a list of DialogSession objects:
public class DialogUser
{
public int Id { get; set; }
public List<DialogSession> DialogSessions { get; set; }
}
I add DialogSession object to the list and then execute context.SaveChanges() as follows:
dialogUser.DialogSessions.Add(dialogSession);
context.SaveChanges();
But the foreign key of the dialogSession record still Null:
I've tried using many methods on the web like the follows but withoyt success:
context.DialogUsers.Attach(dialogUser);
context.Entry(dialogUser).State = EntityState.Modified;
context.SaveChangesExtention();
Does anyone know how to save inner objects (like the list) in the database using Entity Framework (6)?
From your question is not clear which relationship type do you have, so I guess you have One-to-Many and something like this should works:
public class DialogSession
{
public int DialogSessionId { get; set; }
public virtual DialogUser DialogUser { get; set; }
}
public class DialogUser
{
public int DialogUserId { get; set; }
public virtual ICollection<DialogSession> DialogSessions { get; set; }
}
Take a look at example how properly configure this type of relationship in this article.
If I am not wrong you should add
dialogUser.DialogSessions.Add(dialogSession);
context.Entry(dialogUser).State = EntityState.Modified;
context.SaveChanges();
This will mark the entity as modified and then the changes should be reflected on the db.
This could be done a more efficiently by marking singular properties as modified
dialogUser.DialogSessions.Add(dialogSession);
context.Entry(dialogUser).Property(u => u.dialogSession).IsModified = true;
context.SaveChanges();
Give it a try :)
Please see: https://msdn.microsoft.com/en-us/library/jj591583(v=vs.113).aspx
You should use a virtual list for the child entity, and ensure that the DialogSessions class also refers back to its parent with a DialogUserId property (so named by convention)
The below works for me.
using System.ComponentModel.DataAnnotations;
public class DialogSession
{
[Key]
public int DialogSessionId { get; set; }
public int DialogUser { get; set; }
}
public class DialogUser
{
[Key]
public int DialogUserId { get; set; }
public virtual ICollection<DialogSession> DialogSessions { get; set; }
}
I have a model that looks like this:
public class Parent
{
public string Name { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public string Name { get; set; }
public Parent Parent { get; set; }
public Child ParentChild { get; set; }
public List<Child> Children { get; set; }
}
That means that I have a parent that can have children. The children can also have children. Each child has a reference to the parent it belongs to (even if it is a child of a child).
I have the following in my database:
Parent
Child_1
Child_1_1
Child_1_1_1
I now want to add Child_1_2.
var child = new Child(){ Name = "Child 1.2" };
child.ParentChild = child_1;
child.Parent = parent;
context.Children.Add(child);
context.SaveChanges();
My problem is now that this will change Child_1_1 and Child_1_1_1. The reference to their parent will be lost after this Code. They will be null in the database.
This has something to do with Entity Framework not loading all the references (especially not references that are nested).
Is there a way to do this and tell the Entity Framework to add the Parent but not change the parent or any of its children?
My guess is that you are not showing the exact model here. Your model would mean two different tables.
A quick fix would be to add parent.Children.Add(child).
Although I would severely recommend you to redo your model into something similar (although still doesn't make sense in a larger contex):
public class Person
{
public string Name { get; set; }
public List<Child> Children { get; set; }
}
public class Parent : Person
{
// Probably this class would contain something useful
}
public class Child : Person
{
public Person ParentPerson { get; set; }
}
I was curious if it is possible to map an intermediate table through a containing object.
public class Subscriber : IEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
private ChannelList _subscribedList { get; set; }
public int NumSubscribedChannels { get { return _subscribedList.Count(); } }
}
public class HelpChannel : IEntity
{
[Key]
public int Id { get; set; }
public string name { get; set; }
public string category { get; set; }
public int group { get; set; }
}
I need to have a subscriber table, channel table and an intermediate table to link a subscriber to his/her channels.
Is it possible to map the list that is within the ChannelList object to the Subscriber Model?
I figured that's probably not possible and that I would need to just have a private List for EF to map. But I wasn't sure if EF will do that for private variables. Will it?
I'm hoping that is does because if it has to be public to maintain the encapsulation.
You can map private properties in EF code-first. Here is a nice description how to do it. In your case it is about the mapping of Subscriber._subscribedList. What you can't do is this (in the context's override of OnModelCreating):
modelBuilder.Entity<Subscriber>().HasMany(x => x._subscribedList);
It won't compile, because _subscribedList is private.
What you can do is create a nested mapping class in Subscriber:
public class Subscriber : IEntity
{
...
private ICollection<HelpChannel> _subscribedList { get; set; } // ICollection!
public class SubscriberMapper : EntityTypeConfiguration<Subscriber>
{
public SubscriberMapper()
{
HasMany(s => s._subscribedList);
}
}
}
and in OnModelCreating:
modelBuilder.Configurations.Add(new Subscriber.SubscriberMapping());
You may want to make _subscribedList protected virtual, to allow lazy loading. But it is even possible to do eager loading with Include:
context.Subscribers.Include("_subscribedList");
I have these two classes:
public class Parent
{
public virtual string Name { get; set; }
public virtual string Family_id { get; set; }
}
public class Children
{
public virtual string Name { get; set; }
public virtual DateTime BirthDate { get; set; }
public virtual string Family_id { get; set; }
}
When I fetch a parent, I also want to fetch the oldest (ordered by BirthDate) children that has the same Family_id as the parent.
There is no foreign key between the parent and the children in the database.
(I do not want to use two different repositories, I want the functionality in the mappings)
Is property-ref something I can use?
One strategy would be to force an Eager Load on a Children collection and create another property to get the oldest child.
Property-Ref is used to join to another table using a column which is not the primary key.
public class Parent
{
public virtual int Id {get; set;}
public virtual string Name { get; set; }
public virtual string Family_id { get; set; }
public virtual Children OldestChild {
get {
return Children.OrderBy(x=>x.BirthDate).FirstOrDefault();
}}
public virtual IList<Children> Children {get; set;}
}
public class ParentMap : ClassMap<Parent>{
public ParentMap(){
Id(x=>x.Id);
Map(x=>x.Name);
HasMany(x=>x.Children).PropertyRef("Family_id").Fetch.Join();
}
}
Another possibility would be to add a column to the Parent table (OldestChild_FK) and then join in that row from the Children table.
I think what you want to do is create a property on the Parent called OldestChild or a list of Oldest Children and ignore that property and write some custom query (HQL or SQL) to return the results you want.
Here is a thread on ignoring properties in FluentNhibernate.