EF serializes entity to json with included related entities creates a loop - c#

I am using EF in my WebApi project. I have a database including many tables which are related to each other. When I serialize objects coming from tables, it creates a weird json.
My EF query is like below.
db.Products.Include(x => x.ProductCategoryRelations)
.Include(x => x.ProductCategoryRelations.Select(c => c.Category))
.Include(x => x.ProductFileRelations)
.Include(x => x.ProductFileRelations.Select(c => c.File))
.Include(x => x.ProductPropertyRelations)
.Include(x => x.ProductPropertyRelations.Select(c => c.Property))
.Include(x => x.ProductColorRelations)
.Include(x => x.ProductColorRelations.Select(c => c.Color))
.Include(x => x.Brand)
.Where(predicate)
.ToListAsync();
Because of this EF query. It creates a json like below, which is not acceptable...
1.Product
2.Brand
3.Product
4.ProductCategoryRelations
5.Product
.....
.....
.....
.....
How can I fix this? I would like to have array of products but I am not what I need to change to get such a result. Any help would be appreciated.
Thanks in advance

Try this method:
db.Products
.Where(predicate)
.Select(p=> new { p.Brand, p.ProductColor.Color_Name, p.ProductCategory.Category_Name })
.ToListAsync();

Related

EF Core Linq query containing Any cannot be translated [duplicate]

I have procedure which returns me entity's ids which I need.
(I decide to create this procedure, because entities which should be returned to the end user, are filtered by related entities, but EF Core does not support filtering by related entities).
Then I want to use this ids to get entities I need witch theirs related entities.
I'm using the Any operator. In my opinion it should generate query like this: WHERE id IN(1,2,3,4....)` but it seems it does not work like I want.
Instead, it returns warning with the information that the:
Any clause could not be translated and will be evaluated locally
How I could fix it?
My code below:
var assocsIds = await _context.Assoc.AsNoTracking()
.FromSql($"exec {SqlProcedures.GetAssocsForIndexProc} {query.IndexMviId}, {query.FilterByGroupId}")
.ToListAsync()
var productAssocs = await _context.Assoc
.Where(x => assocsIds.Any(z => z.Id == x.Id))
.Include(x => x.Attribute)
.ThenInclude(x => x.Translation)
.Include(x => x.Option)
.ThenInclude(x => x.Translation)
.Include(x => x.Mvi)
.ThenInclude(x => x.Translation)
.AsNoTracking()
.ToListAsync()
;
Can you first select "Id"s from assocsIds into another variable and then try the following?
var productAssocs = await _context.Assoc
.Where(x => yourIdsList.Contains(x.Id))
.Include(x => x.Attribute)
.ThenInclude(x => x.Translation)
.Include(x => x.Option)
.ThenInclude(x => x.Translation)
.Include(x => x.Mvi)
.ThenInclude(x => x.Translation)
.AsNoTracking()
.ToListAsync();

Entity Framework Core filter query with aggregate functions in the Database

I have an entity Projekt with many related entities that have to be included.
I want to filter these Projekte with the filter clause in the where statement.
Unfortunately, it doesn't let me do this without calling .ToList() first which means, that all the Projekt with all the related entities are loaded first which is a performance problem.
How is is possible to filter the entities with the where-clause before calling .ToList() so that it is filtered on the database instead of inmemory?
var projekteWithBudgetUeberschreitung = this.db.Projekt
.Include(x => x.ProjektMitarbeiter)
.Include(x => x.ProjektPhase)
.ThenInclude(x => x.ProjektPhaseResource)
.Include(x => x.ProjektIstWerte)
.Include(x => x.ProjektStatus)
.ToList()
.Where(x => Math.Abs(x.ProjektIstWerte.Sum(y => y.ProjektkostenIst_CHF) ?? 0) >= (decimal)0.75 * Math.Abs(x.PersonalExternPlan_CHF_Freigegeben))
.ToList();
thanks in advance

Multiple levels with theninclude not returning all rows unless I query the detail

I'm using .net core 2.0 (I know) and I've got nested data I'm pulling from sql.
The weird thing is that some of the data isn't showing up unless I explicitly search for it.
var record = db.Records
.Include(x => x.ParentLabels)
.Include(x => x.TopChildren)
.ThenInclude(x => x.LevelTwoChildren)
.ThenInclude(x => x.LevelThreeChildren)
.ThenInclude(x => x.LevelTwoChildren.LevelTwoLabels)
.FirstOrDefault(x => x.Id.Equals(id));
The weird thing is that some of the LevelTwoChildren.Labels data does not show up, UNLESS I add the following:
var t = record.TopChildren.FirstOrDefault();
var tt = t.LevelTwoChildren.FirstOrDefault(x => x.Id.Equals(id2));
I've tried doing a foreach through all the record levels but that doesn't help.
Any ideas?
I guess it is happening because of .ThenInclude(x => x.LevelTwoChildren.LevelTwoLabels). Include statements should include one property at a time. What you should do is:
var record = db.Records
.Include(x => x.ParentLabels)
.Include(x => x.TopChildren)
.ThenInclude(x => x.LevelTwoChildren)
.ThenInclude(x => x.LevelThreeChildren)
.Include(x => x.TopChildren)
.ThenInclude(x => x.LevelTwoChildren)
.ThenInclude(x => x.LevelTwoLabels)
.FirstOrDefault(x => x.Id.Equals(id));

Lambda Expression to filter Included Entities

I have this query,
var productCategories = _dbContext
.Set<ProductCategory>()
.Include(x => x.ProductInCategories)
.ThenInclude(x => x.Product)
.ThenInclude(x => x.ProductMetadatas)
.Where(x => x.Active)
.ToList();
But, I can't filter products in this query. I just want to have the Active Products. Any Help will be appreciated.

Loading all the children entities with entity framework

I have a data model like this
I would like to load all the related entities from a Reconciliation into a Reconciliation object.
For now the only way I could find to load all the related entites to a single Recon is in multiple Lists. But I want to load every related entities in a Reconciliation object. If possible in an elegant way.
Reconciliation recon = db.Reconciliations
.Where(r => r.ReconNum == 382485).First();
List<ReconciliationDetail> reconDetails = recon.ReconciliationDetails.ToList();
List<JrnlEntryDetail> jrnlDetails = reconDetails.Select(r => r.JrnlEntryDetail).ToList();
List<JrnlEntry> jrnl = jrnlDetails.Select(j => j.JrnlEntry).ToList();
List<ARInvoice> invoices = jrnl.SelectMany(j => j.ARInvoices).ToList();
List<ARInvoiceDetail> invoicesDetail = invoices
.SelectMany(i => i.ARInvoiceDetails).ToList();
List<ARCredMemo> credmemos = jrnl.SelectMany(j => j.ARCredMemoes).ToList();
List<ARCredMemoDetail> credmemosDetail = credmemos
.SelectMany(c => c.ARCredMemoDetails).ToList();
List<IncomingPay> incomingPays = jrnl.SelectMany(j => j.IncomingPays).ToList();
List<IncomingPayDetail> incomingPaysDetail = incomingPays
.SelectMany(i => i.IncomingPayDetails).ToList();
// ... and so on for outgoing pays, AP Invoices AP Cred Memo ...etc
I have also tried to load it with Include and Select but I get this exception :
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
And I don't get how I could load every childs of JrnlEntry using Include and Select
Reconciliation recon = db.Reconciliations
.Where(r => r.ReconNum == 382485)
.Include(r => r.ReconciliationDetails
.Select(d => d.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry)
.SelectMany(j => j.ARInvoices).SelectMany(i => i.ARInvoiceDetails))
Edit
Managed to do it this way too but it's not very beautiful :
Reconciliation recon = db.Reconciliations
.Where(r => r.ReconNum == 382485)
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.ARInvoices.Select(i => i.ARInvoiceDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.ARCredMemoes.Select(c => c.ARCredMemoDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.IncomingPays.Select(i => i.IncomingPayDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.OutgoingPays.Select(o => o.OutgoingPayDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.APInvoices.Select(o => o.APInvoiceDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.APCredMemoes.Select(o => o.APCredMemoDetails)))
.Include(r => r.ReconciliationDetails.Select(rd => rd.JrnlEntryDetail)
.Select(jd => jd.JrnlEntry).Select(j => j.JrnlEntryDetails))
There are two ways to perform Eager Loading in Entity Framework:
ObjectQuery.Include Method
Explicit loading using either DbEntityEntry.Collection Method or DbEntityEntry.Reference Method along with their respective Load methods
There are also manners to write Raw SQL Queries against database:
DbSet.SqlQuery Method deals with entities
Database.SqlQuery Method deals with arbitrary types
Database.ExecuteSqlCommand Method for arbitrary DDL/DML
For the case, when you're attempting to load nearly entire database, it would be good idea to execute dedicated store procedure against it.
Try with just .Include(r => r.ReconciliationDetails) initially. Then add the .Select() statements one-by-one. At what point does the exception reappear? The .SelectMany() call looks a bit suspicious to me!
A second question that might help identify the problem... After you run the code that contains all the ToList() calls, is your recon entity complete? i.e. are all its navigation properties populated? This should be the case because of the automatic 'fixup' behavior of Entity Framework.
With EF, sometimes it is more efficient to load a complex object graph with several calls rather than chained Include() calls. Check the generated SQL and see what is most efficient in your case.
Not sure if it's to late but could you benefit from structuring your code such as
var acctName = "someName";
var detailList = _repository.Include(e => e.JrnlEntryDetail).Filter(c => c.JrnlEntryDetail.Any(e => e.AcctName == acctName)).Get().ToList();

Categories

Resources