I want to build a query with where condition for filter properties when they have a value (not null). The multiple filters must be a AND combination if they are used.
How can combine/add each predicate when the according property value is not null to get a final query ready for filtering?
IQueryable<Customer> filter = null;
Expression<Func<Customer, bool>> predicatetest1 = res => res.test1 == request.test1;
Expression<Func<Customer, bool>> predicatetest2 = res => res.test2 == request.test2;
Expression<Func<Customer, bool>> predicatetest3 = res => res.test3 == request.test3;
if (request.test1 != null)
{
// add the above predicate to the filter
}
if (request.test2 != null)
{
// add the above predicate to the filter
}
if (request.test3 != null)
{
// add the above predicate to the filter
}
IQueryable<Customer> filter = Context.Customers;
if (request.test1 != null)
{
filter = filter.Where(predicatetest1);
}
if (request.test2 != null)
{
filter = filter.Where(predicatetest2);
}
if (request.test3 != null)
{
filter = filter.Where(predicatetest3);
}
var customers = filter.ToList();
The following would be equivalent, when all 3 properties where not null
Context.Customers.Where(predicatetest1).Where(predicatetest2).Where(predicatetest3).ToList();
Related
My generic repository is as follows ( I have similar in synchronious and it works)
public virtual async Task<IEnumerable<TEntity>> GetAsyn(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return await orderBy(query).ToListAsync();
}
else
{
return await query.ToListAsync();
}
}
when I call this:
var custDB = await unitOfWork.CustomerRepository
.GetAsyn()
.Where(c => c.UserId == userID && c.IsDeleted != true)
.FirstOrDefault();
I get the following error...Can someone help me fix this
...Error CS1061 'Task<IEnumerable>' does not contain a
definition for 'Where' and no accessible extension method 'Where'
accepting a first argument of type 'Task<IEnumerable>' could
be found (are you missing a using directive or an assembly
reference?) ..
Because GetAsyn() return Task. You need await result of task.
var result = await unitOfWork.CustomerRepository.GetAsyn()
var custDB = result.Where(c => c.UserId == userID && c.IsDeleted != true)
.FirstOrDefault();
You could make this work by calling
var customers = await unitOfWork.CustomerRepository.GetAsyn();
var customer = customers.Where(c => c.UserId == userID && c.IsDeleted != true).FirstOrDefault();
But that would perform the filtering on the client. And your "generic repository" doesn't really do anything useful, so you should just delete that code and run:
var customer = await db.Customers.Where(c => c.UserId == userID && c.IsDeleted != true).FirstOrDefaultAsync();
GetAsyn returns a task, you should await it first.
But anyway why would you do the filtering on the client? Your GetAsyn method has a filter parameter, so you should write it like this:
var custDbList = await unitOfWork.CustomerRepository
.GetAsyn(c => c.UserId == userID && c.IsDeleted != true)
var custDB = custDbList.FirstOrDefault();
am not sure if this is still valid or needed ,but here is my input
you just need the ToListAsync() method, which is in the System.Linq.Async package .
some code to help
using part :
using System;
sing System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
code :
public async Task<JsonResult> getgaAreasList(string silter)
{
return Json(await new gaAreasVM()
.GetAll()
.Where(x=>x.name == filter)
.ToListAsync());
}
I am having some issues creating a CompositeFilterDescriptor from scratch.
The scenario: We have date filters on the grid, but are showing date time fields.
When Someone filters to a specific date, we show all records for that day.
when someone filters greater than that day, we edit the filterDescriptor for that filter and add one day to it.
when someone filters to less than or equal to that day, we edit the filterdescriptor for that filter and add one day to it.
Here is the problem: when someone selects "not equal to", the solution would be to create a compositeFilterDescriptor containing two filters. One for less than that day, one for greater than that day.
I may ore may not be doing this part properly. I can not figure out how to add the new compositeFilterDescriptor to the existing dataSourceRequest.
Here is what I have:
static public DataSourceRequest FixDateFilters(DataSourceRequest request, string[] optionalFields = null)
{
if (request?.Filters != null)
{
List<string> matchingFields = new List<string> { "utc", "time" };
List<FilterDescriptor> newDescriptors = new List<FilterDescriptor>();
//if any fields specific to a page have been passed in, append them to the list.
if (optionalFields != null)
{
foreach (string field in optionalFields)
matchingFields.Append(field);
}
foreach (string matchingField in matchingFields)
{
List<FilterDescriptor> descriptors = new List<FilterDescriptor>();
FilterDescriptor descriptor1 = FindFirstFilterByMember(request.Filters, matchingField, true, null);
//there could be up to two filters on a matching field. Get the 2nd one if it exists.
FilterDescriptor descriptor2 = FindFirstFilterByMember(request.Filters, matchingField, true, descriptor1);
//turn the matches into a list for iterating
if (descriptor1 != null)
descriptors.Add(descriptor1);
if (descriptor2 != null)
descriptors.Add(descriptor2);
if (descriptors.Count != 0)
{
foreach (FilterDescriptor descriptor in descriptors)
{
DateTime? utcDate = (DateTime?)descriptor?.Value;
if (utcDate.HasValue)
{
utcDate = Core.DateHelper.ToUTC(utcDate.Value, HttpContext.Current); //make sure date ranges are one day ranges in local time
}
if (utcDate != null)
{
if (descriptor.Operator == FilterOperator.IsLessThanOrEqualTo)
{
utcDate = ((DateTime)utcDate).AddDays(1);
descriptor.Value = utcDate;
}
else if (descriptor.Operator == FilterOperator.IsGreaterThan)
{
utcDate = utcDate.Value.AddDays(1);
descriptor.Value = utcDate;
}
else if (descriptor.Operator == FilterOperator.IsEqualTo)
{
descriptor.Operator = FilterOperator.IsGreaterThanOrEqualTo;
newDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsLessThan, utcDate.Value.AddDays(1)));
}
else if (descriptor.Operator == FilterOperator.IsNotEqualTo)
{
CompositeFilterDescriptor cfd = new CompositeFilterDescriptor();
cfd.LogicalOperator = FilterCompositionLogicalOperator.Or;
cfd.FilterDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsLessThan, utcDate.Value;));
cfd.FilterDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsGreaterThan, utcDate.Value.AddDays(1)));
newDescriptors.Add( cfd);
}
}
}
}
}
//equals matches add a new descriptor so add them after the foreach is done so they don't affect the foreach.
foreach (FilterDescriptor newDescriptor in newDescriptors)
{
if (newDescriptor.Member != "") //a blank descriptor has a emptry string for member
{
request.Filters.Add(newDescriptor);
}
}
}
return request;
}
}
static public FilterDescriptor FindFirstFilterByMember(IEnumerable<IFilterDescriptor> filters, string findMember, bool partialMatch = false, FilterDescriptor previous = null)
{
FilterDescriptor ret = null;
foreach (var filter in filters)
{
var descriptor = filter as FilterDescriptor;
if (descriptor != null
&& (descriptor.Member == findMember || (partialMatch == true && descriptor.Member.ToLower().Contains(findMember.ToLower())))
&& (previous == null || previous != descriptor))
{
ret = descriptor;
break;
}
else if (filter is CompositeFilterDescriptor)
{
ret = FindFirstFilterByMember(((CompositeFilterDescriptor)filter).FilterDescriptors, findMember, partialMatch, previous);
if (ret != null)
break;
}
}
return ret;
}
The second function is a recursive one for getting the filters by string.
The problem section is the "isNotEqualTo" comparison. I don't know how to save the composite filter into the existing request. It gives me a casting error.
Ok it turns out all i needed to do was create a new list of CompositeFilterDescriptor type and append that when necessary. The .add function took both. Sigh.
foreach (CompositeFilterDescriptor newCompositeDescriptor in newCompositeDescriptors)
{
request.Filters.Add(newCompositeDescriptor);
}
I at this moment I have repository filled with multiple gets methods.
E.q. Get1() => cxt.Entites.Include(e => e.obj1);
Get2() => cxt.Entities.Include(e => e.obj1).Include(e => e.obj2)
And so on.
Is there good method, pattern to have one GET method where I can send inclues via parameter?
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
See repository pattern in msdn
You can use
_sampleRepostiory.Get(h=>h.Id>1,null,"Employees.Departments");
Including same with lambda
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
if (includes != null)
{
query = includes.Aggregate(query,
(current, include) => current.Include(include));
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
Consume it like this
var query = context.Customers
.Get(x=>x.Id>1,null,
c => c.Address,
c => c.Orders.Select(o => o.OrderItems));
Similar SO question
I did the following in my projects:
public Entity[] GetAll(bool includeObj1, bool includeAllOthers) {
IQueryable<Entity> entity = ctx.Entities;
if (includeObj1)
entity = entity.Include(e => e.obj1);
if (includeAllOthers) {
entity = entity
.Include(e => e.obj2)
.Include(e => e.obj3)
.Include(e => e.obj4)
.Include(e => e.obj5);
}
return entity.ToArray();
}
Providing arguments like includeObj1 and includeObj2 separates a consumer of repository from implementation and encapsulates any data access logic.
Passing direct "include these properties" orders to a repository means that you know how repository works and assume that it is some sort ORM which blurs abstractions.
I'm new to JPA with the CriteriaBuilder and want to reduce some lines of code.
cb.and is usefull to add predicate's, but what if they are null?
How can I reduce these lines of code? Thanks!
I'm worried about the readability of the code...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(OrganizationEntity.class);
Root<OrganizationEntity> root = cq.from(OrganizationEntity.class);
Predicate predicateFrom = null;
Predicate predicateTo = null;
if (from != null) {
predicateFrom = cb.ge(root.get(OrganizationEntity_.id), from);
}
if (to != null) {
predicateTo = cb.le(root.get(OrganizationEntity_.id), to);
}
if(predicateFrom != null && predicateTo != null) {
cq.where(cb.and(predicateFrom,predicateTo));
} else if (predicateFrom != null && predicateTo == null) {
cq.where(predicateFrom);
} else if (predicateFrom == null && predicateTo != null) {
cq.where(predicateTo);
}
TypedQuery<OrganizationEntity> query = em.createQuery(cq);
list = query.getResultList();
return list;
what you could do is create your predicate list
List<Predicate> predicates = new ArrayList<>();
if (from != null) {
predicates .add(cb.ge(root.get(OrganizationEntity_.id), from));
}
if (to != null) {
predicates .add(cb.le(root.get(OrganizationEntity_.id), to));
}
if (predicates.size()>0{
cq.where(cb.and(predicates.toArray(new Predicate[predicates
.size()])));
}
Basically I retrieved the records from the table and wanted to updated one column.
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.Select(c => new { c.FilePath, c.FileName })
.ToList();
So this query has only two fields: FilePath and FileName, then next I want to assign FilePath = null;
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();
Then I got an error:
Property or indexer 'AnonymousType#1.FilePath' cannot be assigned to -- it is read only
You select from query anonymous class. You cannot set properties of anonymous class. To do what you want you should get whole entity:
var query = cdrContext.tabless.Where(c => c.FacilityID == facilityID && c.FilePath != null && c.TimeStationOffHook < oldDate)
.OrderBy(c => c.TimeStationOffHook)
.Skip(size)
.Take(pageSize)
.ToList();
foreach (var y in query)
{
y.FilePath = null;
}
cdrContext.SaveChanges();