.NET CORE 2 EF Include - c#

I am using new .net core and EF.
I need help with include linq command. I have some 1:N models and if the collection contais some data marked like deleted I do not want to include them.
How to do it?
var company = await _context.Company
.Include(y => y.Administrators)
.Include(y => y.CompanyPartTimers)
.Include(z => z.WorkPlaces)
.Include(z => z.Requirements)
.FirstAsync(x => x.Id == id);
If I add the condition
.Include(z => z.WorkPlaces).Where(x=>x.WorkPlaces.Where(x=>!x.IsDeleted))
It doesn't work. How to write this correctly?
Next thing is I have IDeletable Interface and it would be better if I had some custom linq expression and could do for ex.
.Include(z => z.WorkPlaces).GetNonDeleted()
Does anyone know how to do it?
I tryed something like this
public static class LinqExtension
{
public static IEnumerable<T> GetActive<T>(this IEnumerable<T> source) where T : class, IDeletable
{
return source.Where(x => x.IsDeleted);
}
}
Thanks guys.

You can configure a Query Filter in your DbContext.
modelBuilder.Entity<Administrator>()
.HasQueryFilter(admin => !EF.Property<boolean>(admin, "IsDeleted"));
should do the trick
Reference link: https://learn.microsoft.com/en-us/ef/core/querying/filters

You should change the inner condition using Any instead of Where, as:
.Include(z => z.WorkPlaces)
.Where(x => x.WorkPlaces.Any(y => !y.IsDeleted))

Related

Allow the use of class function in where-expression

Are there any way to get the following expression to work in EF Core 3.1?
_db.Customers
.Where(customer => customer.IsFavorite(userId))
.ToList();
IsFavorite is a simple expression that compares multiple properties with the userId. This could obviously be rewritten as
_db.Customers
.Where(customer => customer.Prop1 == userId || customer.Prop2 == userId)
.ToList();
But i'd like to avoid duplicating this logic. Other uses are when filtering on child properties, such as:
_db.Orders
.Where(order => order.Customer.IsFavorite(userId))
.ToList();
Any suggestions?

Entity Framework Fetch Parent of Parent

I have the following data model:
Agenda->AgendaSection->SectionItem
Starting with the Item, I need to fetch both the Section and Agenda.
I've tried the following (and other things):
IList<AgendaSectionItem> myTasks =
db.AgendaSectionItems.Where(i => i.AssigneeId == currentUser.UserId)
.Include(i => i.AgendaSection)
.Include(s => s.AgendaSection.Agenda)
.ToList();
However, the Agenda ends up being null.
Anyone know how to do this?
Thanks,
Philip
Try removing the first include statement. I have had issues in the past with multiple include calls for whatever reason. It should work with just the one call:
IList<AgendaSectionItem> myTasks =
db.AgendaSectionItems.Where(i => i.AssigneeId == currentUser.UserId)
.Include(s => s.AgendaSection.Agenda)
.ToList();

Entity Framework 6 - include derived type child property

My schema includes:
LoanApplication (parent object)
Borrowers (child object that as derived type BusinessBorrowerIndividual)
BorrowersOwnerManagers (many-to-many, I have to explicitly define it because it has custom audit properties)
OwnerManager (part of many-to-many relationship with Borrowers)
OwnerManagerIndividual (derived type from OwnerManager)
OwnerManagerBusiness (derived type from OwnerManager)
OwnerManagerIndividualsRaceTypes (many-to-many, I have to explicitly define it because it has custom audit properties)
RaceType (part of many-to-many relationship with OwnerManagerIndividual)
I tried using the following in my repository for LoanApplication:
public ILoanApplication GetById(int id)
{
ILoanApplication item;
using (var db = new MainContext())
{
item = db.LoanApplications
.Include(c => c.Borrowers)
.Include(c => c.Borrowers.Select(b => b.BusinessBorrowerIndividual))
.Include(c => c.Borrowers.Select(b => b.BorrowersOwnerManagers))
.Include(c => c.Borrowers.Select(b => b.BorrowersOwnerManagers.Select(bb => bb.OwnerManager)))
.Include(c => c.Borrowers.Select(b => b.BorrowersOwnerManagers.Select(bb => bb.OwnerManager)
.OfType<OwnerManagerIndividual>()
.Select(o => o.OwnerManagerIndividualsRaceTypes)))
.FirstOrDefault(l => l.Id == id);
}
return item;
}
Everything works except for the last include. I receive the following error when I use it:
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. Parameter name: path
How do I update so that I can include the OwnerManagerIndividualsRaceTypes properties for the OwnerManager objects that are of OwnerManagerIndividual type? I also want to include all OwnerManger objects, regardless of if they are OwnerManagerIndividual or OwnerManagerBusiness.
** Update **
Based on the responses, it appears it can't easily be done in a single call. I going with the suggestion to make a secondary call to populate, which isn't as elegant solution:
public ILoanApplication GetById(int id)
{
var ownerManagerIndividualRepository = new OwnerManagerIndividualRepository(UserOrProcessName);
ILoanApplication item;
using (var db = new MainContext())
{
item = db.LoanApplications
.Include(c => c.Borrowers)
.Include(c => c.Borrowers.Select(b=> b.BusinessBorrowerIndividual))
.Include(c => c.Borrowers.Select(b => b.BorrowersOwnerManagers))
.Include(c => c.Borrowers.Select(b => b.BorrowersOwnerManagers.Select(bb => bb.OwnerManager)))
.FirstOrDefault(l => l.Id == id);
}
if (item?.Borrowers?.Count > 0 &&
item.Borrowers.FirstOrDefault().BorrowersOwnerManagers.Any(o => o.OwnerManager.IsIndividual))
{
foreach (var owners in item.Borrowers.FirstOrDefault().BorrowersOwnerManagers.Where(o => o.OwnerManager.IsIndividual))
{
var owner = ownerManagerIndividualRepository.GetById(owners.OwnerManager.Id.Value);
foreach (var type in owner.OwnerManagerIndividualsRaceTypes)
{
((OwnerManagerIndividual)owners.OwnerManager).OwnerManagerIndividualsRaceTypes.Add((OwnerManagerIndividualsRaceTypes)type);
}
}
}
return item;
}
.Include() extension serves just single purpose: load properties eagerly instead of leaving them for lazy loading. That's just an option for query builder to include JOINs in SQL, not anything else.
You are trying to provide filtering inside Include expression, which is wrong.
As for how to actually achieve filtering part of inner collection I'm not sure that's easily doable with single query in EF, you can check similar question for example How to filter nested collection Entity Framework objects?

LastOrDefault vs FirstOrDefault [duplicate]

In this query:
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderBy(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.Last());
}
I had to switch it to this for it to work
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.FirstOrDefault());
}
I couldn't even use p.First(), to mirror the first query.
Why are there such basic limitations in what's otherwise such a robust ORM system?
That limitation comes down to the fact that eventually it has to translate that query to SQL and SQL has a SELECT TOP (in T-SQL) but not a SELECT BOTTOM (no such thing).
There is an easy way around it though, just order descending and then do a First(), which is what you did.
EDIT:
Other providers will possibly have different implementations of SELECT TOP 1, on Oracle it would probably be something more like WHERE ROWNUM = 1
EDIT:
Another less efficient alternative - I DO NOT recommend this! - is to call .ToList() on your data before .Last(), which will immediately execute the LINQ To Entities Expression that has been built up to that point, and then your .Last() will work, because at that point the .Last() is effectively executed in the context of a LINQ to Objects Expression instead. (And as you pointed out, it could bring back thousands of records and waste loads of CPU materialising objects that will never get used)
Again, I would not recommend doing this second, but it does help illustrate the difference between where and when the LINQ expression is executed.
Instead of Last(), Try this:
model.OrderByDescending(o => o.Id).FirstOrDefault();
Replace Last() by a Linq selector OrderByDescending(x => x.ID).Take(1).Single()
Something like that would be works if you prefert do it in Linq :
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}
Yet another way get last element without OrderByDescending and load all entities:
dbSet
.Where(f => f.Id == dbSet.Max(f2 => f2.Id))
.FirstOrDefault();
That's because LINQ to Entities (and databases in general) does not support all the LINQ methods (see here for details: http://msdn.microsoft.com/en-us/library/bb738550.aspx)
What you need here is to order your data in such a way that the "last" record becomes "first" and then you can use FirstOrDefault. Note that databasese usually don't have such concepts as "first" and "last", it's not like the most recently inserted record will be "last" in the table.
This method can solve your problem
db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();
Adding a single function AsEnumerable() before Select function worked for me.
Example:
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName).AsEnumerable()
.Select(p => p.FirstOrDefault());
Ref:
https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys

OrderByDescending with a date from another table(s)

Hopefully this question is not to confusing. Basically I'm looking for pointers on how to OrderByDecending with a date from relational tables. I have constructed a basic method that looks like it could possibly work but I'm getting errors:
DbSortClause expressions must have a type that is order comparable.
Parameter name: key
I understand what this is saying but I'm not entirely sure how to fix using Linq method syntax.
public BusinessEntities.Application GetLastUpdatedAppliction(int userID)
{
return context.tbl_User_To_Application
.Where(x => x.UserID == userID)
.OrderByDescending
(o => o.tbl_Application.tbl_ApplicationChanges
.Where(oo => oo.ApplicationID == o.ApplicationID)
.Select(s => s.ChangeDate))
.ThenByDescending(t => t.DateAdded)
.Select(y => new BusinessEntities.Application
{
ApplicationID = y.tbl_Application.ApplicationID,
ApplicationName = y.tbl_Application.ApplicationName
}).FirstOrDefault();
}
Basically I have a cross reference table that binds a user to a specific application(Website) Then inside I need to nest into two tables to get the latest changes to the Application with a "ChangesDate". So ideally this would return the last updated application. Then obviously populates my DTO.
I'm still trying to get to grips with Linq method syntax so any help would be greatly appreciated!
Regards,
Tez Wingfield
If you want to order by the last application change date:
(...)
.OrderByDescending(o =>
o.tbl_Application.tbl_ApplicationChanges
.Where(ac => ac.ApplicationID == o.ApplicationID)
.OrderByDescending(ac => ac.ChangeDate)
.First()
.Select(ac => ac.ChangeDate)
)
(...)

Categories