LinQ to query an entity including children with conditions - c#

What is wrong in this query? It's about 2 entities that can be logically deleted by inserting a delete date. So I must be sure I get a single Entity with is collection of SubEntities. All with DateDelete not null.
return DbContext.Entity
.AsNoTracking()
.Include(y => y.SubEntities.Select(sb => sb.DateDelete == null))
.Single(y => y.Name == entityName && y.DateDelete == null);
On runtime I get an 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. Parameter name: path
I also tried this with same error
return DbContext.Entity
.AsNoTracking()
.Include(y => y.SubEntities.Where(sb => sb.DateDelete == null))
.Single(y => y.Name == entityName && y.DateDelete == null);

After my own investigation I found it is impossible to filter on .Include() but there are workarounds by using .Select() method or the filter library.
Workaround 1
return DbContext.Entity
.AsNoTracking()
.Where(e => e.DateDelete == null)
.Select(e => new
{
e.Id,
e.Property,
e.DateDelete,
SubEntities = e.SubEntities.Where(se => se.DateDelete == null)
})
.ToList();
Workaround 2
The solution given by Gert Arnold here with EntityFramework.DynamicFilters. This workaround has some limitation. Read the documentation.

Related

Filter List of a model using linq

I have a Model with List inside it, is there any way to filter that list?
What I'm trying is:
List<Booking> a = await _context.Set<Booking>().Include(u => u.BookingLine).Include(u => u.BookingStatus).ToListAsync();
Inside that Booking model, there is a List. I just want to get all the StatusId = 1 inside that BookingStatus.
Booking model and BookingStatus model has the same BookingId. I don't know where do I put the Where() inside that linq.
I tried like this but it returned an error:
_context.Set<Booking>().Include(u => u.BookingLine).Include(u => u.BookingStatus
.LastOrDefault(a => a.StatusId == 1 || a.StatusId == 7 || a.StatusId == 13)).ToListAsync();
Error:
System.InvalidOperationException: The expression 'u.BookingStatus.AsQueryable().LastOrDefault(a => (((a.StatusId == 1) OrElse (a.StatusId == 7)) OrElse (a.StatusId == 13)))' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data
I think you meant to use "Where" instead of "LastOrDefault".
something like:
_context.Set<Booking>().Include(u => u.BookingLine).Include(u => u.BookingStatus
.Where(a => a.StatusId == 1 || a.StatusId == 7 || a.StatusId == 13)).ToListAsync();
Microsoft example of Eager Loading:
using (var context = new BloggingContext())
{
var filteredBlogs = context.Blogs
.Include(
blog => blog.Posts
.Where(post => post.BlogId == 1)
.OrderByDescending(post => post.Title)
.Take(5))
.ToList();
}

Using conditional statements in linq query c#

I have 2 queries. First gets the full name of a committee head if the committee has more that 1 member.
var result = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Where(f => f.ExpertCommitteeMembers.Count > 1)
.Select(f => f.ExpertCommitteeMembers
.Where(m => m.IsCommitteeHead)
.FirstOrDefault().Expert.FullName)
.FirstOrDefaultAsync();
The second one gets the full name of the only committee member if the committee has only 1 member
var result2 = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Where(f => f.ExpertCommitteeMembers.Count == 1)
.Select(f => f.ExpertCommitteeMembers
.FirstOrDefault().Expert.FullName)
.FirstOrDefaultAsync();
Is it possible to check how many members does the committee have and then return the correct name all in the same query? Or do I first have to check how many members does the committee have and then run the appropriate query seperatly?
If I understand correctly you can try to let condition into inner linqwhere
var result = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Select(f => f.ExpertCommitteeMembers.Where(m =>
(m.IsCommitteeHead &&
f.ExpertCommitteeMembers.Count > 1)||f.ExpertCommitteeMembers.Count == 1).FirstOrDefault().Expert.FullName)
.FirstOrDefaultAsync();
you can Create a create anonymous object in linq query select. which will contain FullName and count.
var result = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Where(f => f.ExpertCommitteeMembers.Count > 1)
.Select(f => new
{
FullName = f.ExpertCommitteeMembers
.Where(m => m.IsCommitteeHead)
.FirstOrDefault().Expert.FullName,
Count = f.ExpertCommitteeMembers.Count
})
.FirstOrDefaultAsync();
A generated Join, as it is generated from ExpertCommittees to
ExpertCommitteeMembers, with a Navigation-Collection, will always do a Left Join,
what you want is an Inner Join. It will give you only items with entities in both tables.
This will be something like
db.ExpertCommittees.Join(db.ExpertCommittemember, x=>someid, y=>somid,
(comittee, member) => new { comittee, member});
But this will give you one line per member.... with the possibility to filter by "IsComitteeHead" or whatever.
You can append Grouping, or use a GroupJoin directly, to have a List of "Commitees", each with a List of it's members ...and only if there are members.
Without knowing the data structure, ID's, Foreign-Keys and table name, we cannot produce a valid query.

Filtering query by child entity element

I used entity framework with my example. I wanted to filter child entity but I am getting 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.'
public List<Notification> GetNotificationBySentDate(DateTime? dateTime)
{
if (dateTime == null)
{
return
_dbContext.Notifications.Include(x => x.Attachments.Select(a=>a.Clean==true))
.Where(x =>
x.Sent == null &&
x.FaultCount <= _appSettingsHelper.NotificationsFaultCountLimit &&
DbFunctions.AddSeconds(x.CreatedDate, x.DelaySeconds) < DateTime.UtcNow)
.OrderBy(a => DbFunctions.AddSeconds(a.CreatedDate, a.DelaySeconds))
.Take(_appSettingsHelper.NotificationsBySentStateSelectTop).ToList();
}
return _dbContext.Notifications.Include(x => x.Attachments).Where(x => x.Sent >= dateTime)
.OrderBy(a => a.CreatedDate)
.Take(_appSettingsHelper.NotificationsBySentStateSelectTop).ToList();
}
Any help would be much appreciated. Thanks.
It seems that the problem is with Select statement under the Include. You can try to remove it and add an additional clause to Where, something like && x.Attachements.Clean == true. Thus your code will be
_dbContext.Notifications.Include(x => x.Attachments)
.Where(x =>
x.Sent == null &&
x.FaultCount <= _appSettingsHelper.NotificationsFaultCountLimit &&
DbFunctions.AddSeconds(x.CreatedDate, x.DelaySeconds) < DateTime.UtcNow &&
x.Attachments.Clean == true)
.OrderBy(a => DbFunctions.AddSeconds(a.CreatedDate, a.DelaySeconds))
.Take(_appSettingsHelper.NotificationsBySentStateSelectTop).ToList();

linq predicate remove objects from collection issue

I am trying to figure out why my delegate function does not work, any help would be appreciated, this is probably a small issue but I have been working on it for a while and connot figure it out, my code:
//remove all matching people from this list
public void RemovePeopleFromLookup(Predicate<PeopleDTO> _people)
{
//Lookup is an internal readonly ICollection of PeopleDTO
Lookup.RemoveAll(_people);
}
//call the method as below:
//data is a collection of PeopleDTO
mylookupobj.RemovePeopleFromLookup(x => data.Any(y => y.Name == x.Name && x.Type == FieldElement.Strange));
For some reason all people get removed from this lookup, this is not correct, I only want to remove the people who are
Strange
who DONOT exist in the data collection
EDIT:
The data collection can be an object of different types -> strange, noisy etc...
The mylookupobj.Lookup data collection is similar to the data collection and contain contain multiple types hence why I wrote my query that way
EDIT2: I missed out this information which may be very important...
public class PersonDTO
{
//Name
//Type
//Age
//Desc
}
Inside the mylookupobj.Lookup - all the properties contain data, however inside the data collection only the Name + Type is present.
A simpler and more efficient predicate would be:
x => (x.Type == FieldElement.Strange) && data.Any(y => y.Name == x.Name)
But I admit I don't see a principal problem with either.
Edit: seems 1 of the conditions has to be inverted.
x => (x.Type == FieldElement.Strange) && ! data.Any(y => y.Name == x.Name)
The call to Any is the problem. Essentially it's running through the collection multiple times. i.e. if any of the objects in the collection matches the condition, remove the item. Try this:
mylookupobj.RemoveFieldFromLookup(y => y.Name == x.Name && x.Type == FieldElement.Strange);
Please try this.
mylookupobj.RemovePeopleFromLookup(x => data.Contains(y => y.Name == x.Name)
&& x.Type == FieldElement.Strange);

Linq to SQL / C#: How to Order By using property in cross reference table with lambda expressions

I am trying to order a list of products based on the zindex property of the cross reference table with the category table (in this case called 'Chassis'), but I get the following error:
Cannot order by type 'System.Collections.Generic.IEnumerable`1[System.Int32]'.
The following is the method I am using:
public IQueryable<E_Product> Product_GetList_ByChassisId(int chassisId)
{
return dc.E_Products
.Where(x => x.Deleted == false)
.Where(x => x.Published == true)
.Where(x => x.E_Product_Chassis
.Any(c => c.ChassisId == chassisId && c.Deleted == false))
.OrderBy(x => x.E_Product_Chassis.Select(c => c.Zindex));
}
I understand the .Select method returns an IEnumerable, but being a many-to-many relationship, x.E_Product_Chassis does not allow simple selection of its properties (e.g. x.E_Product_Chassis.Zindex).
Any help would be very appreciated...
FirstOrDefault(), Min(), Max() -- use one of these functions to select the appropriate z-index out of the set.
public IQueryable<E_Product> Product_GetList_ByChassisId(int chassisId)
{
return dc.E_Products
.Where(x => x.Deleted == false)
.Where(x => x.Published == true)
.Where(x => x.E_Product_Chassis
.Any(c => c.ChassisId == chassisId && c.Deleted == false))
.OrderBy(x => x.E_Product_Chassis.Min(c => c.Zindex));
}

Categories

Resources