A followup question to this: IQueryable Extensions working on expression for collection property
I am working on a project for a family member which involves the use of a database and a repository, and I have set up a fairly complex (possibly too complex, but I don't have a ton to go off of so I'm trying to make it very flexible for rapid expansion) system to allow for searching for items through the repository via the use of various SearchParameters
objects, based off of the following base class to allow for pagination and direct item finding via GUID:
Notes:
- Using Entity Framework Code First
- EFCF model classes make use of navigation properties
public abstract class SearchParametersBase <TModel> : SearchBase
{
public List<Guid> ItemGuids { get; set; }
protected SearchParametersBase(int page, int size, params Guid[] itemGuids) : base(page, size)
{
if (itemGuids == null || !itemGuids.Any())
ItemGuids = null;
else ItemGuids = itemGuids.ToList();
}
}
The biggest problem I see currently is that because I have search parameter classes such as these:
public class CompanySearchParameters : SearchParametersBase<Company>
{
public string BillingAddressSearchParameter { get; set; }
public ClientSearchParameters ClientSearchParameters { get; set; }
public LocationSearchParameters LocationSearchParameters { get; set; }
public string NameSearchParameter { get; set; }
public CompanySearchParameters(int page, int size, params Guid[] itemGuids) : base(page, size, itemGuids)
{ }
}
public class LocationSearchParameters : SearchParametersBase<CompanyLocation>
{
public string AddressSearchParameter { get; set; }
public CompanySearchParameters CompanySearchParameters { get; set; }
public string DescriptionSearchParameter { get; set; }
public string LabelSearchParameter { get; set; }
public LocationSearchParameters(int page, int size, params Guid[] itemGuids) : base(page, size, itemGuids)
{ }
}
I ended up with a helper class that has a fair amount of duplicated code based on a different type of IQueryable<T>
for a model that has navigation properties:
internal static class RepositoryQueryFilterer
{
internal static IQueryable<Account> FilterAccountQuery(IQueryable<Account> query, AccountSearchParameters searchParameters)
{
if (searchParameters == null)
return query;
if (!string.IsNullOrWhiteSpace(searchParameters.NotesSearchParameter))
query = query.Where(x => x.Notes.Contains(searchParameters.NotesSearchParameter));
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<Client> FilterClientQuery(IQueryable<Client> query, ClientSearchParameters searchParameters)
{
if (searchParameters == null)
return query;
query = FilterClientQueryByAccountSearch(query, searchParameters.AccountSearchParameters);
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<Client> FilterClientQueryByAccountSearch(IQueryable<Client> query, AccountSearchParameters searchParameters)
{
if (searchParameters == null)
return query;
if (!string.IsNullOrWhiteSpace(searchParameters.NotesSearchParameter))
query = query.Where(x => x.Account.Notes.Contains(searchParameters.NotesSearchParameter));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
internal static IQueryable<Company> FilterCompanyQuery(IQueryable<Company> query, CompanySearchParameters searchParameters)
{
if (searchParameters == null)
return query;
query = FilterCompanyQuery(query, searchParameters.ClientSearchParameters);
query = FilterCompanyQuery(query, searchParameters.LocationSearchParameters);
if (!string.IsNullOrWhiteSpace(searchParameters.NameSearchParameter))
query = query.Where(x => x.Name.Contains(searchParameters.NameSearchParameter));
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<Company> FilterCompanyQuery(IQueryable<Company> query, ClientSearchParameters searchParameters)
{
if (searchParameters == null) return query;
query = FilterCompanyQuery(query, searchParameters.AccountSearchParameters);
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => x.Clients.Any(y => searchParameters.ItemGuids.Contains(y.Id)));
return query;
}
internal static IQueryable<Company> FilterCompanyQuery(IQueryable<Company> query, AccountSearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.NotesSearchParameter))
query = query.Where(x => x.Clients.Any(y => y.Account.Notes.Contains(searchParameters.NotesSearchParameter)));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => x.Clients.Any(y => searchParameters.ItemGuids.Contains(y.AccountId)));
return query;
}
internal static IQueryable<Company> FilterCompanyQuery(IQueryable<Company> query, LocationSearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.DescriptionSearchParameter))
query = query.Where(x => x.Locations.Any(y => y.Description.Contains(searchParameters.DescriptionSearchParameter)));
if (!string.IsNullOrWhiteSpace(searchParameters.LabelSearchParameter))
query = query.Where(x => x.Locations.Any(y => y.Label.Contains(searchParameters.LabelSearchParameter)));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => x.Locations.Any(y => searchParameters.ItemGuids.Contains(y.Id)));
return query;
}
internal static IQueryable<Contact> FilterContactQuery(IQueryable<Contact> query, ClientSearchParameters searchParameters)
{
if (searchParameters == null) return query;
query = FilterContactQuery(query, searchParameters.AccountSearchParameters);
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.ClientId));
return query;
}
internal static IQueryable<Contact> FilterContactQuery(IQueryable<Contact> query, CompanySearchParameters searchParameters)
{
if (searchParameters == null) return query;
query = FilterContactQuery(query, searchParameters.LocationSearchParameters);
query = FilterContactQuery(query, searchParameters.ClientSearchParameters);
query = FilterContactQuery(query, searchParameters.LocationSearchParameters);
if (!string.IsNullOrWhiteSpace(searchParameters.NameSearchParameter))
query = query.Where(x => x.Name.Contains(searchParameters.NameSearchParameter));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
internal static IQueryable<Contact> FilterContactQuery(IQueryable<Contact> query, AccountSearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.NotesSearchParameter))
query = query.Where(x => x.Client.Account.Notes.Contains(searchParameters.NotesSearchParameter));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Client.AccountId));
return query;
}
internal static IQueryable<Contact> FilterContactQuery(IQueryable<Contact> query, LocationSearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.DescriptionSearchParameter))
query = query.Where(x => x.Company.Locations.Any(y => y.Description.Contains(searchParameters.DescriptionSearchParameter)));
if (!string.IsNullOrWhiteSpace(searchParameters.LabelSearchParameter))
query = query.Where(x => x.Company.Locations.Any(y => y.Label.Contains(searchParameters.LabelSearchParameter)));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => x.Company.Locations.Any(y => searchParameters.ItemGuids.Contains(y.Id)));
return query;
}
internal static IQueryable<Contact> FilterContactQuery(IQueryable<Contact> query, ContactSearchParameters searchParameters)
{
if (searchParameters == null)
return query;
query = FilterContactQuery(query, searchParameters.ClientSearchParameters);
query = FilterContactQuery(query, searchParameters.CompanySearchParameters);
if (!string.IsNullOrWhiteSpace(searchParameters.CellNumberSearchParameter))
query = query.Where(x => x.CellNumber.Contains(searchParameters.CellNumberSearchParameter));
if (!string.IsNullOrWhiteSpace(searchParameters.OfficeNumberSearchParameter))
query = query.Where(x => x.OfficeNumber.Contains(searchParameters.OfficeNumberSearchParameter));
if (!string.IsNullOrWhiteSpace(searchParameters.EmailSearchParameter))
query = query.Where(x => x.Email.Contains(searchParameters.EmailSearchParameter));
if (!string.IsNullOrWhiteSpace(searchParameters.NameSearchParameter))
query = query.Where(x => x.Name.Contains(searchParameters.NameSearchParameter));
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<Discount> FilterDiscountQuery(DiscountSearchParameters searchParameters, IQueryable<Discount> query)
{
if (searchParameters == null)
return query;
query = FilterDiscountQuery(searchParameters.ProductSearchParameters, query);
if (searchParameters.IsPercentSearchParameter != null)
query = query.Where(x => x.IsPercent == searchParameters.IsPercentSearchParameter.Value);
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<Discount> FilterDiscountQuery(ProductSearchParameters searchParameters, IQueryable<Discount> query)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.TitleSearchParameter))
query = query.Where(x => x.Product.Title.Contains(searchParameters.TitleSearchParameter));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
internal static IQueryable<CompanyLocation> FilterLocationQuery(IQueryable<CompanyLocation> query, LocationSearchParameters searchParameters)
{
if (searchParameters == null)
return query;
query = FilterLocationQuery(query, searchParameters.CompanySearchParameters);
if (!string.IsNullOrWhiteSpace(searchParameters.DescriptionSearchParameter))
query = query.Where(x => x.Description.Contains(searchParameters.DescriptionSearchParameter));
if (!string.IsNullOrWhiteSpace(searchParameters.LabelSearchParameter))
query = query.Where(x => x.Label.Contains(searchParameters.LabelSearchParameter));
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<CompanyLocation> FilterLocationQuery(IQueryable<CompanyLocation> query, CompanySearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.NameSearchParameter))
query = query.Where(x => x.Company.Name.Contains(searchParameters.NameSearchParameter));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
internal static IQueryable<Order> FilterOrderQuery(IQueryable<Order> query, OrderSearchParameters searchParameters)
{
if (searchParameters == null)
return query;
query = FilterOrderQuery(query, searchParameters.CompanySearchParameters);
query = FilterOrderQuery(query, searchParameters.ProductSearchParameters);
if (!string.IsNullOrWhiteSpace(searchParameters.NotesSearchParameter))
query = query.Where(x => x.Notes.Contains(searchParameters.NotesSearchParameter));
if (searchParameters.PositionsOfInterestSearchParameter != null && searchParameters.PositionsOfInterestSearchParameter.Any())
query = query.Where(x => x.PositionsOfInterest.Any(y => searchParameters.PositionsOfInterestSearchParameter.Contains(y)));
if (searchParameters.OrderStatusSearchParameter != null && searchParameters.OrderStatusSearchParameter.Any())
query = query.Where(x => searchParameters.OrderStatusSearchParameter.Contains(x.Status));
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<Order> FilterOrderQuery(IQueryable<Order> query, CompanySearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.NameSearchParameter))
query = query.Where(x => x.Company.Name.Contains(searchParameters.NameSearchParameter));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
internal static IQueryable<Order> FilterOrderQuery(IQueryable<Order> query, ProductSearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (!string.IsNullOrWhiteSpace(searchParameters.TitleSearchParameter))
query = query.Where(x => x.Product.Title.Contains(searchParameters.TitleSearchParameter));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
internal static IQueryable<Product> FilterProductQuery(IQueryable<Product> query, ProductSearchParameters searchParameters)
{
if (searchParameters == null)
return query;
FilterProductQuery(query, searchParameters.DiscountSearchParameters);
if (!string.IsNullOrWhiteSpace(searchParameters.TitleSearchParameter))
query = query.Where(x => x.Title.Contains(searchParameters.TitleSearchParameter));
query = FilterGuids(query, searchParameters);
return query;
}
internal static IQueryable<Product> FilterProductQuery(IQueryable<Product> query, DiscountSearchParameters searchParameters)
{
if (searchParameters == null) return query;
if (searchParameters.IsPercentSearchParameter != null)
query = query.Where(x => x.Discounts.Any(y => y.IsPercent == searchParameters.IsPercentSearchParameter.Value));
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
private static IQueryable<TModel> FilterGuids<TModel>(IQueryable<TModel> query, SearchParametersBase<TModel> searchParameters)
where TModel : PocoBase
{
if (searchParameters.ItemGuids != null && searchParameters.ItemGuids.Any())
query = query.Where(x => searchParameters.ItemGuids.Contains(x.Id));
return query;
}
}
The reason that, so far, I haven't been able to combine those methods that look, and in fact are, almost identical, is that they are operating on different objects with the related navigation properties.
Is there a way of making these methods more single-minded and reusable?