0

I'm trying to use the entity framework to filter my GET method... Using the if the condition it's working

 public async Task<List<TimeEntryViewModel>> Get(TimeEntryFilter filter)
        {
            var result = await _readRepository
                .FindByCondition(x => x.IsApproved == true)
                .Include(x => x.Task)
                .Include(x => x.Account)
                .Select(x => new TimeEntryViewModel
                {
                    AccountName = x.Account.Name,
                    Date = x.Date,
                    StartTime = x.StartTime,
                    FinishTime = x.FinishTime,
                    InternalId = x.InternalId,
                    TaskId = x.TaskId,
                    TaskName = x.Task.Name,
                    CreatedBy = x.CreatedBy,
                    CustomerName = x.Task.Project.ServiceOrder.Customer.Name
                }).ToListAsync().ConfigureAwait(false);

            if  (filter.AccountName != null || 
                (filter.StartDate.HasValue && filter.FinishDate.HasValue && (filter.StartDate <= filter.FinishDate)) ||
                (filter.TaskName != null) ||
                (filter.CustomerName != null) ||
                (filter.InternalId != null))
                result = result.Where(x => 
                                        (x.AccountName.ToString().Contains(filter.AccountName)) &&
                                        (x.Date >= filter.StartDate && x.Date <= filter.FinishDate) &&
                                        (x.CustomerName.ToString().Contains(filter.CustomerName)) &&
                                        (x.TaskName.ToString().Contains(filter.TaskName)) &&
                                        (x.InternalId.ToString().Contains(filter.InternalId.ToString()))).ToList();

            return result;
        }

But I imagine that there is a way to improve this method without using if

I'd had already tried to do that (Just filter account name to show) But returned Internal Server Error

var result = await _readRepository
                .FindByCondition(x => x.IsApproved == true)
                .Where(x => string.IsNullOrEmpty(filter.AccountName) || x.Account.Name.ToString().Contains(filter.AccountName))
                .Include(x => x.Task)
                .Include(x => x.Account)
                .Select(x => new TimeEntryViewModel
                {
                    Id = x.Id,
                    AccountName = x.Account.Name,
                    Date = x.Date,
                    Description = x.Description,
                    StartTime = x.StartTime,
                    FinishTime = x.FinishTime,
                    InternalId = x.InternalId,
                    OnSite = x.OnSite,
                    IsApproved = x.IsApproved,
                    IsBillable = x.IsBillable,
                    TaskId = x.TaskId,
                    TaskName = x.Task.Name,
                    CreatedBy = x.CreatedBy,
                    CustomerName = x.Task.Project.ServiceOrder.Customer.Name
                }).ToListAsync().ConfigureAwait(false);

Okay Guys EDIT1: In my second sample, using InternalId instead of Account.Name

.Where(x => (string.IsNullOrEmpty(filter.InternalId.ToString())) || x.InternalId.ToString().Contains(filter.InternalId.ToString()))

it worked.... I believe that I'm having trouble to work with something that is a foreign key in my table...

4 Answers4

1

There is a very interesting way to mass multiple bool values (contains or not etc) with only one value. It's called bit bitwise operations. Here: Most common C# bitwise operations on enums you can find different approachs to this "technique".

Iavor Orlyov
  • 512
  • 1
  • 4
  • 15
1

It returned null because you don't check if Account property is null.

 AccountName = x.Account.Name

So the above will throw a Null Reference Exception.

A way to avoid this, it is to use the null conditional operator:

AccountName = x.Account?.Name

You can follow the same approach also for properties like:

  • Task.Name
  • Task.Project.ServiceOrder.Customer.Name

And access them safely, by using the same operator, like below:

  • Task?.Name
  • Task?.Project?.ServiceOrder?.Customer?.Name

Based on the above you can follow the way you have already followed for AccountName property, but you have also at the same time to use the null conditional operator.

Christos
  • 53,228
  • 8
  • 76
  • 108
1

It looks like you need && operator:

.Where(x => string.IsNullOrEmpty(filter?.AccountName) 
    && x?.Account?.Name.ToString().Contains(filter.AccountName))

because when you use || operator, then you can get NullReferenceException in the second part of your expression in Where:

x?.Account?.Name.ToString().Contains(filter.AccountName))
StepUp
  • 36,391
  • 15
  • 88
  • 148
0

Okay, apparently, my problem was .Where(x => string.IsNullOrEmpty(filter.AccountName) || x.Account.Name.ToString().Contains(filter.AccountName)) after x.Account.Name I can't use ToString()... I don't know exactly why, but now works without the if condition

public async Task<List<TimeEntryViewModel>> GetApproved(TimeEntryFilter filter)
        {
            var result = await _readRepository
                .FindByCondition(x => x.IsApproved == true)
                .Include(x => x.Task)
                .Include(x => x.Account)
                .Where(x => 
                            (string.IsNullOrEmpty(filter.InternalId.ToString()) || x.InternalId.ToString().Contains(filter.InternalId.ToString())) &&
                            (string.IsNullOrEmpty(filter.AccountName) || x.Account.Name.Contains(filter.AccountName)) && 
                            (string.IsNullOrEmpty(filter.CustomerName) || x.Task.Project.ServiceOrder.Customer.Name.Contains(filter.CustomerName)) &&
                            (string.IsNullOrEmpty(filter.TaskName) || x.Task.Name.Contains(filter.TaskName)))
                .Select(x => new TimeEntryViewModel
                {
                    Id = x.Id,
                    AccountName = x.Account.Name,
                    Date = x.Date,
                    Description = x.Description,
                    StartTime = x.StartTime,
                    FinishTime = x.FinishTime,
                    InternalId = x.InternalId,
                    OnSite = x.OnSite,
                    IsApproved = x.IsApproved,
                    IsBillable = x.IsBillable,
                    TaskId = x.TaskId,
                    TaskName = x.Task.Name,
                    CreatedBy = x.CreatedBy,
                    CustomerName = x.Task.Project.ServiceOrder.Customer.Name
                }).ToListAsync().ConfigureAwait(false);

            return result;
        }

If anyone knows why it's working, I'll be thankful