I have the following 2 classes:
public class Rule
{
public int Id { get; set; }
public string RuleValue { get; set; }
public bool IsActive { get; set; }
public SharedRuleType RuleType { get; set; }
public List<Exclusion> Exclusions { get; set; }
}
public class Exclusion
{
public int Id { get; set; }
public int InstanceId { get; set; }
public int SiteId { get; set; }
[ForeignKey( "RuleId" )]
public int RuleId { get; set; }
public Rule Rule { get; set; }
}
I then have an EF query that brings back 'all active' Rules
, and I need it to .Include Exclusions
for each Rule
(if there are any) BUT only Exclusions
that have been assigned the specified InstanceId
. So the filtering is being done against the Exclusions
property, rather than filtering out Rules
.
I also have a few conditions as I build up my EF query that I need to take into consideration.
Here is my query at the moment:
public async Task<List<Rule>> GetRules(int instanceId, SharedRuleType ruleType, string searchTerm)
{
using ( var context = new MyDbContext() )
{
var query = context.Set<Rule>()
.Include( r => r.Exclusions ) // *** Currently returns ALL exclusions but I only want ones where InstanceId == instanceId(param) ***
.Where( r => r.IsActive );
if ( !string.IsNullOrEmpty( searchTerm ) )
{
query = query.Where( r => r.RuleValue.Contains( searchTerm ) );
}
if ( ruleType != SharedRuleType.None )
{
query = query.Where( r => r.RuleType == ruleType );
}
return await query.ToListAsync();
}
}
I tried applying a .Where within the .Include()
in an attempt to only include the relevant Exclusions
(based on instanceId
) but found out you can't do that. I hunted around and found some examples where people had used an anonymous type, but I couldn't get this working when building up the query piece by piece as I'm doing here.
So, I don't know how I can accomplish this as I really don't want to be returning 'every' Exclusion
for each Rule
, when I don't need every Exclusion
returned.
The Include method cannot use a filter like you tried.
Solution #1
Disclaimer: I'm the owner of the project Entity Framework Plus
EF+ Query IncludeFilter feature allows filtering related entities.
public async Task<List<Rule>> GetRules(int instanceId, SharedRuleType ruleType, string searchTerm)
{
using ( var context = new MyDbContext() )
{
var query = context.Set<Rule>()
.IncludeFilter( r => r.Exclusions.Where(x => x.InstanceId == instanceId))
.Where( r => r.IsActive );
// ... code ...
Wiki: EF+ Query IncludeFilter
Solution #2
Another technique is to use projection (which is what my library do under the hood)
public async Task<List<Rule>> GetRules(int instanceId, SharedRuleType ruleType, string searchTerm)
{
using ( var context = new MyDbContext() )
{
var query = context.Set<Rule>()
.Where( r => r.IsActive );
if ( !string.IsNullOrEmpty( searchTerm ) )
{
query = query.Where( r => r.RuleValue.Contains( searchTerm ) );
}
if ( ruleType != SharedRuleType.None )
{
query = query.Where( r => r.RuleType == ruleType );
}
// ToListAsync has been removed to make the example easier to understand
return query.Select(x => new { Rule = x,
Exclusions = x.Exclusions.Where(e => e.InstanceId == instanceId)
})
.ToList()
.Select(x => x.Rule)
.ToList();
}
}
EDIT: Answer Sub-Questions #1
How to use ToListAsync with the previous example
You simply have to await for the first list
return (await query.Select(x => new { Rule = x,
Exclusions = x.Exclusions.Where(e => e.InstanceId == instanceId)
})
.ToListAsync())
.Select(x => x.Rule)
.ToList();
EDIT: Answer Sub-Questions #2
How to perform Skip, Take, OrderBy on Rule
You do the same as you normally do
return (await query.Take(15)
.Skip(5)
.OrderBy(x => x.RuleId)
.Select(x => new { Rule = x,
Exclusions = x.Exclusions.Where(e => e.InstanceId == instanceId)
})
.ToListAsync())
.Select(x => x.Rule)
.ToList();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With