Ho un modello con solo 3 classi: User
, Filter
e FilterEntry
.
class Filter
{
public List<FilterEntry> Inclusions { get; set; }
public List<FilterEntry> Exclusions { get; set; }
}
public class FilterEntry
{
public string Name { get; set; }
public int? Age { get; set; }
}
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
Un Filter
è un filtro che è stato mantenuto nel DB, è possibile vederlo come una definizione di un filtro. Contiene uno o più FilterEntries
che definiscono il filtro. Le Inclusioni e le esclusioni sono le restrizioni che si applicano al filtro.
Un esempio:
var filter = new Filter
{
Inclusions = new[] { new FilterEntry { Age = 33 } },
Exclusions = new[] { new FilterEntry { Name = "John" }, new FilterEntry { Name = "Peter" } },
};
Questo definisce un Filter
che rappresenterà le persone di età compresa tra 33 anni, ad eccezione di chi è chiamato "John" o "Peter". Quindi, quando questo filtro viene applicato agli utenti, dovrebbe restituire tutti gli utenti 33, ad eccezione di Johns e Peters.
Ora il problema Come posso creare una query utilizzando Entity Framework che, dato un filtro, restituisce gli utenti in base ad esso?
Non so nemmeno come iniziare! Tutto ciò che ho è questo:
Filter filter = dbContext.Filters.First(x => x.FilterId == filterId);
var filteredUsers = from u in dbContext.Users
where ... // user is any of the in filter.Inclusions
where ... // user is not in any of the filter.Exclusions
select u;
AVVISO che Filter e FilterEntry mantengono una relazione 1-N. Ho omesso le chiavi per semplificare il codice.
Volete creare una clausola where dinamica, quindi consiglierei PredicateBuilder
. Non ho testato il codice qui sotto, ma vorresti qualcosa come ...
Filter filter = dbContext.Filters.First(x => x.FilterId == filterId);
var pb = new PredicateBuilder<User>();
foreach(var inclusion in filter.Inclusions)
{
if(inclusion.Age.HasValue)
{
pb = pb.And(p => p.Age == inclusion.Age.Value);
}
if(!string.IsNullOrWhiteSpace(inclusion.Name))
{
pb = pb.And(p => p.Name == inclusion.Name);
}
}
foreach(var exclusion in filter.Exclusions)
{
if(exclusion.Age.HasValue)
{
pb = pb.And(p => p.Age != exclusion.Age.Value);
}
if(!string.IsNullOrWhiteSpace(exclusion.Name))
{
pb = pb.And(p => p.Name != exclusion.Name);
}
}
var filteredUsers = dbContext.Users.AsExpandable().Where(pb);
Ho avuto uno scenario simile dove volevo essere in grado di specificare filtri dinamici come quello. Ho finito per utilizzare il pacchetto System.Linq.Dynamic.Core
System.Linq.Dynamic.Core per aiutare ( https://github.com/StefH/System.Linq.Dynamic.Core ). Ciò consente di eseguire query Linq utilizzando gli input di stringa. Quindi, invece di dover costruire un albero di espressioni Linq dal Filter
, è sufficiente costruire una clausola where come stringa.
public class Filter
{
public List<FilterEntry> Inclusions { get; set; }
public List<FilterEntry> Exclusions { get; set; }
public IQueryable<User> ApplyTo<User>(IQueryable<User> queryable)
{
// TODO: Dynamically build this string instead of hard-coding it.
// You would probably iterate through Inclusions and Exclusions
// appending to the where clause as you go.
var whereClause = "Age == @0 && Name != @1 && Name != @2";
var params = new object[] { 33, "John", "Peter" };
return queryable.Where(whereClause, params);
}
}
Mi rendo conto che questa non è una soluzione funzionante, ma spero che almeno ti dia un punto di partenza.