Include children and grandchildren when selecting from database - entity-framework

I have the following models
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Child> Child { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<GrandChild> GrandChild { get; set; }
}
public class GrandChild
{
public int Id { get; set; }
public string Name { get; set; }
}
What Im trying to do now is select a list of parents from the database with the children and grandchildren.
Tried the following with no joy:
List<Parent> parent = new List<Parent>();
parent = db.parent.ToList();

Use the Include method:
parent = db.parent.Include(parent => parent.Child.Select(child => child.GrandChild)).ToList();
For older versions of Entity Framework, you have to use a string instead of a lambda expression:
parent = db.parent.Include("Child.GrandChild").ToList();
Or you can use the custom Include extension method I blogged about here.

in Linq to Sql you should use DataLoadOptions
var dlo = new DataLoadOptions();
dlo.LoadWith<Parent>(p => p.Child );
dlo.LoadWith<Child>(c=> c.GrandChild );
db.LoadOptions = dlo;
List<Parent> parent = new List<Parent>();
parent = db.parent.ToList();

Related

Include object of child object using MongoDB driver .net core

I want to include object in the main collection's child object, I tried below code but it didn't work.
Can someone help?
Here I want to include "Sample" object in "Template" when getting collection of "Instance".
public class Instance
{
public long IId { get; set; }
public string Name { get; set; }
public long TemplateId { get; set; }
public Template Template { get; set; }
}
public class Template
{
public long TId { get; set; }
public string Name { get; set; }
public long SampleId { get; set; }
public Sample Sample { get; set; }
}
public class Sample
{
public long SId { get; set; }
public string Name { get; set; }
}
string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("test");
var instances = db.GetCollection<Instance>("Instances");
var resultOfJoin = instances.Aggregate()
.Lookup("Template", "TemplateId", "TId", #as: "Template")
.Lookup("Sample", "SampleId", "SId", #as: "Template.Sample")
.Unwind("Template")
.Unwind("Template.Sample")
.As<Instance>()
.ToList();
I found the solution by try and error, We need to add a local filed key with the object name as below,
.Lookup("Sample", "Template.SampleId", "SId", #as: "Template.Sample")
var resultOfJoin = instances.Aggregate()
.Lookup("Template", "TemplateId", "TId", #as: "Template")
.Lookup("Sample", "Template.SampleId", "SId", #as: "Template.Sample")
.Unwind("Template")
.Unwind("Template.Sample")
.As<Instance>()
.ToList();

Parent object is null when getting child objects using Entity Framework

public class AcsBatchingDbContext : DbContext
{
public DbSet<CardHolder> CardHolders { get; set; }
public DbSet<AccessCard> AccessCards { get; set; }
}
public class CardHolder
{
public int CardHolderId { get; set; }
public ICollection<AccessCard> AccessCards { get; set; };
}
public class AccessCard
{
public int AccessCardId { get; set; }
public CardHolder CardHolder { get; set; }
}
When I try to get AccessCards
using (var db = new AcsBatchingDbContext())
{
var cards = db.AccessCards.ToList();
}
Where card.CardHolder = null
Why? Why EF doesnt grab the CardHolder?
Another question:
Why this expression doesnt compile?
db.AccessCards.Include(x => x.CardHolder).ToList();
Why the only options is to use is
db.AccessCards.Include("CardHolder").ToList();
You should use include to load CardHolder entity.
(This requires using System.Data.Entity;)
db.AccessCards.Include(x => x.CardHolder).ToList();
Alternatively, you can apply Include like this;
db.AccessCards.Include("CardHolder").ToList();

EF Core Include() doesn't query all childs

I have this model:
public class RepairRequest
{
[Key]
public int Id { get; set; }
public List<RepairAction> RepairActions { get; set; }
public decimal TotalPrice => RepairActions.Sum(r => r.ActionPrice);
public string LastOperation => RepairActions.LastOrDefault().RepairOperation.Description;
}
public class RepairAction
{
[Key]
public int Id { get; set; }
public int RepairRequestId { get; set; }
public RepairRequest RepairRequest { get; set; }
public int RepairOperationId { get; set; }
public RepairOperation RepairOperation { get; set; }
public decimal ActionPrice { get; set; }
}
public class RepairOperation
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
}
I'm trying to query RepairRequests and get TotalPrice and also LastOperation in a List but doesn't work for both properties. This is what I have tried till now:
using (var context = new ServiceManagerContext(new DbContextOptions<ServiceManagerContext>())) {
var data = context.RepairRequests
.Include(r => r.RepairActions).ThenInclude(r => r.RepairOperation); // Only LastAction works
//.Include("RepairActions").Include("RepairActions.RepairOperation"); // Only LastAction works
//.Include(r => r.RepairActions); // Only TotalPrice works
//.Include("RepairActions"); // Only TotalPrice works
var repairRequest = data.FirstOrDefault(r => r.Id == 5);
Assert.NotNull(repairRequest);
Assert.Equal(60.0m, repairRequest.RepairPrice);
Assert.Equal("Παραδόθηκε", repairRequest.LastAction);
}
Thank you.
I'd consider avoiding attempting to resolve calculated properties in your domain entities and instead look to resolve those when querying the data to populate view models.
If your view model needs the TotalPrice and LastOperation, then provided a Repository or such returning IQueryable you can expand the query to return what is needed using deferred execution rather than attempting to rely on eager loading the entire tree:
I.e.
IQueryable<RepairRequest> requests = context.RepairRequests.Where(x => x.Id == 5); // Or pull from a Repository returning the IQueryable
var viewModelData = requests.Select(x => new {x.Id, TotalPrice = x.RepairActions.Sum(), LastOperation = x.RepairActions.LastOrDefault()?.RepairOperation?.Description }).SingleOrDefault();
This should execute a more optimized query and return you an anonymous type with just the data you need to populate whatever view model you want to display. The iffy bit is around situations where there are no repair actions, or a repair action without an operation.. EF should avoid the null ref and just return null. the ?. syntax may not be necessary or supported, so it may just need to be ".". Using a method where you eager or lazy load those related entities and execute Linq off the entity instances, be careful around .SingleOrDefault() and drilling down into child fields.
Firstaball you have to declare Foreign Keys, and flag virtual properties like :
public class RepairRequest
{
[Key]
public int Id { get; set; }
public virtual ICollection<RepairAction> RepairActions { get; set; }
public decimal TotalPrice => RepairActions.Sum(r => r.ActionPrice);
public string LastOperation => RepairActions.LastOrDefault().RepairOperation.Description;
}
public class RepairAction
{
[Key]
public int Id { get; set; }
public decimal ActionPrice { get; set; }
public int RepairRequestId { get; set; }
[ForeignKey("RepairRequestId ")]
public virtual RepairRequest RepairRequest { get; set; }
public int RepairOperationId { get; set; }
[ForeignKey("RepairOperationId")]
public RepairOperation RepairOperation { get; set; }
}
Then you could call this, which load all children values :
var data = context.RepairRequests.Include("RepairActions.RepairOperation");

migrating old ado.net web app to EF

consider this query
Select (some properties from all 3 Tables)
From PageWidgets pw LEFT JOIN PageWidgetDefinition pwd
On pwd.ID = pw.WidgetID
LEFT JOIN PageWidgetSkin pws
ON pws.ID = pw.SkinInstanceID
LEFT JOIN PageWidgetSkinRows pwsr
On pwsr.SkinID = pws.ID Where pw.PageID = *x*
Order By (some properties)
in old implementation, it reads widgets on a page & their skin & i have a function looping through rows returned & make a pagewidget by its skin & its widget instance.
each widget has three row for its skin, and finally we receive a List that has everything it needs to operate
i have these classes in EF
public partial class Widget: BaseEntity {
public int ID { get; set; }
public int PageTemplateID { get; set; }
public PageTemplate PageTemplate { get; set; }
public int WidgetDefinitionID { get; set; }
public WidgetDefinition WidgetDefinition { get; set; }
public int WidgetSkinID { get; set; }
public WidgetSkin WidgetSkin { get; set; }
//other properties omitted
}
public partial class WidgetDefinition: BaseEntity {
public int ID { get; set; }
public string Title { get; set; }
//other properties omitted
public virtual ICollection<Widget> Widgets { get; set; }
}
public partial class WidgetSkin: BaseEntity {
public int ID { get; set; }
public string Name { get; set; }
//other properties omitted
public virtual ICollection<Widget> Widgets { get; set; }
public virtual ICollection<WidgetSkinRow> WidgetSkinRows { get; set; }
}
public partial class WidgetSkinRow: BaseEntity {
public int ID { get; set; }
public int WidgetSkinID { get; set; }
public virtual WidgetSkin WidgetSkin { get; set; }
}
do i need an extra bussiness layer doing the same thing?
using EF, I want to have only one trip to DB.
You can use "eager loading" method to do this.
Your query will then look something like this:
using (var entities = new WidgetEntities() )
{
var query = from w in entities.Widgets.Include("WidgetDefinition").Include("WidgetDefinition.Widgets").Include("WidgetSkins").Include("WidgetSkins.WidgetSkinRows")
where w.Page = *x*
order by w.someproperty
select w;
Widget myWidget = query.First();
}

EF code-first: How to load related data (parent-child-grandchild)?

I have this entity:
public class DynamicPage {
public int PageId { get; set; }
public int Order { get; set; }
public string MenuText { get; set; }
public string MenuHover { get; set; }
public int? ParentId { get; set; }
public virtual DynamicPage Parent { get; set; }
public virtual ICollection<DynamicPage> Children { get; set; }
}
This entity may have 3 level: Parent -> Child -> Grandchild. How can I load the Parent (level 1) whit all associated children (level 2) and for each child, associated grandchild (level 3) if any? Thanks to help.
EF 4.1 feature and syntax:
var entity = context.Parents
.Include(p => p.Children.Select(c => c.GrandChildren))
.FirstOrDefault(p => p.Id == 1); // or whatever condition
If you want to make life easy on yourself, follow the EF Code First conventions of naming your table IDs simply Id (or, alternatively, name of table + Id, e.g., DyanmicPageId).
This should leave you with something like this:
public class DynamicPage
{
public int Id { get; set; }
public int Order { get; set; }
public string MenuText { get; set; }
public string MenuHover { get; set; }
public int? ParentId { get; set; }
public virtual DynamicPage Parent { get; set; }
public virtual ICollection<DynamicPage> Children { get; set; }
}
Then you need to set up the relationship between parents and children explicitly in an OnModelCreating method in your DbContext class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DynamicPage>()
.HasMany(page => page.Children)
.WithRequired(child => child.Parent)
.HasForeignKey(child => child.ParentId);
}
You can then select children or grandchildren as needed:
var parent = dbContext.DynamicPages.Where(page => page.ParentId == null);
var children = parent.Children;
var grandchildren = parent.SelectMany(page => page.Children);
var allRelatedPages = parent.Union(children).Union(grandchildren);