Linq query to get the last record - c#

I know this is asked many times and I've searched most of the solutions online, but nothing seems to make it for me. I have a table with this structure:
ID | ScheduleId | Filename | Description
1 | 10 | | ....
2 | 10 | test.txt | .....
I want to get the last non-empty Filename by passing the ScheduleId(e.g. to get "test.txt" in this case).
I've tried many things and nothing seems to get me the Filename. Here is the last one:
var tempFileName = objContext.SchedulesAndFiles
.Where(x => x.ScheduleId == scheduleId)
.OrderByDescending(x => x.ScheduleId)
.Take(1).Select(x => x.Filename);
This doesn't work as well, although I understand why it doesn't:
var tempFileName = from e in objContext.SchedulesAndFiles
where e.ScheduleId == scheduleId
orderby e.ScheduleId descending
select e.Filename;
Calling .Last() or .LastOrDefault() throws an exception(The query operator 'LastOrDefault' is not supported.)

if have to include that you want only non-empty filenames. You may also use ToList() to finalize the query, then FirstOrDefault() should work as expected, try
var tempFileName = objContext.SchedulesAndFiles
.Where(x
=> x.ScheduleId == scheduleId
&& x.Filename != null
&& x.Filename != "")
.OrderByDescending(x => x.ScheduleId)
.Take(1)
.Select(x => x.Filename)
.ToList()
.FirstOrDefault();

You should sort your records based on the ID instead of ScheduleId and also filter the records that has the empty Filename:
objContext.SchedulesAndFiles
.Where(x => x.ScheduleId == scheduleId && x.Filename != "")
.OrderByDescending(x => x.ID)
.First().Filename;

One option is to call ToList() or AsEnumerable() before trying to use LastOrDefault().
var tempFileName = objContext.SchedulesAndFiles
.Where(x => x.ScheduleId == scheduleId
&& x.Filename != null && x.Filename != '')
.ToList().LastOrDefault();
if(tempFileName != null)
{
// Do something
}

You can try this query. I think you have to issue the last or default before selecting the file name
var tempFileName = objContext.SchedulesAndFiles
.Where(x => x.ScheduleId == scheduleId && ! string.IsNullOrEmpty(e.FileName))
.FirstOrDefault().Select(x => x.Filename);

The final attempt:
var tempFileName = objContext.SchedulesAndFiles
.Where(x
=> x.ScheduleId == scheduleId
&& x.Filename != null
&& x.Filename != "")
.OrderByDescending(x => x.ID)
.First()
.Select(x => x.Filename);
For every item with this scheduleId, this gets the all of the items which have a non-empty fileName, sorted descending by ID (assuming that higher IDs are inserted after lower IDs), getting the First() (should be supported) and getting its FileName.
Note that you could run into a NullPointerException on First() if there is no satisfying fileName.
Also, you may need to normalize/trim to not find spaces/tabs and such things.

Related

How can I use optional parameters in a query with multiple joins, using Entity Framework?

I've looked at several possible solutions to this problem, and the ones I have tried do not seem to work. One solution was to use if statements for the optional filters, which doesn't work because I have multiple joins and the where clause is in the last join.
The optional parameters are: roleId, disciplineId, resourceId, and projectName.
try
{
IQueryable<ProjectPlanHeader> bob =
(
from h in context.ProjectPlanHeaders
join r in context.ProjectPlanRevisions on h.ProjectPlanHeaderId equals r.ProjectPlanHeaderId
join a in context.PlanActivityLineItems on r.PlanRevisionId equals a.PlanRevisionId
where ((roleId == null || a.RequiredRoleId == roleId) &&
(disciplineId == null || a.DisciplineId == disciplineId) &&
(resourceId == null || a.ActualResourceId == resourceId) &&
(h.ProjectPlanName.ToLower().Contains(projectName.ToLower()) || projectName == String.Empty))
select h
)
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem)
.ThenInclude(z => z.PlannedHours)
.Include(x => x.ActualPlanRevisions)
.ThenInclude(y => y.ActualPlanActivities)
.ThenInclude(z => z.ActualHours);
var john = bob.ToList();
return bob;
}
catch (Exception ex)
{
return null;
}
I added the try/catch so I could see what was happening, as it was silently failing. What I found was a "Object not set to an instance of an object". That's never helpful, because I don't know what object it's talking about. Can someone please show me how to do this the right way?
UPDATE: Thanks for the responses I got, but unfortunately they don't work. The problem is that I end up getting multiple headers back when I filter. This happens because there are multiple revisions for each header, and I really only need the max rev. I tried changing the initial query so that only the max rev was included, and that still did not help. There does not appear to be a solution for this issue, so I will have to do it another way.
Rewrite query to do not use explicit joins, because you have navigation properties. Also because of JOINS you have duplicated records, you will discover it later.
var query = context.ProjectPlanHeaders
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem)
.ThenInclude(z => z.PlannedHours)
.Include(x => x.ActualPlanRevisions)
.ThenInclude(y => y.ActualPlanActivities)
.ThenInclude(z => z.ActualHours)
.AsQueryable();
if (!string.IsNullOrEmpty(projectName))
{
// here we can combine query
query = query
.Where(h => h.ProjectPlanName.ToLower().Contains(projectName.ToLower()));
}
// check that we have to apply filter on collection
if (roleId != null || disciplineId != null || resourceId != null)
{
// here we have to do filtering as in original query
query = query
.Where(h => h.ProjectPlanRevisions
.Where(r => roleId == null || r.PlanActivityLineItem.RequiredRoleId == roleId)
.Where(r => disciplineId == null || r.PlanActivityLineItem.DisciplineId == disciplineId)
.Where(r => resourceId == null || r.PlanActivityLineItem.ActualResourceId == resourceId)
.Any()
);
}
var result = query.ToList();
Let me clarify my comment with an example:
So: An IQueryable is used to build up an expression tree. They are not evaluated until enummerated (e.g. ToList or FirstOrDefault). I.e. you can conditional add Where and Includes with little to no cost before ennumerating`
Thus you could do this,
IQueryable<ProjectPlanHeader> bob =
context.ProjectPlanHeader
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem);
if (roleId != null) {
bob =
from h in bob
join r in context.ProjectPlanRevisions on h.Id equals r.ProjectPlanHeaderId
join a in context.PlanActivityLineItems on r.Id equals a.ProjectPlanRevisionId
where a.RequiredRoleId == roleId
select h;
}
// same for the rest.
var john = bob.ToList();
writing the chained filer is not easiest, but it works

Linq optional parameters query

I am making a search form that queries my database to show results based on what has been filled out on the form. The only required field is the date which I have working. all the other fields are optional, if an optional field is not filled in it should not be a part of the query. This is the code I have written:
var queryable = context.TransactionJournal.Where(s => s.TransactionDateTime <= transactionDate)
.Where(s => Region == null || Region == s.AcquirerID)
.Where(s => MCC == null || MCC == s.MerchantCategoryCode)
.Where(s => MerchantID == null || MerchantID.Contains(s.MerchantID))
.Where(s => TxnCurrency == null || TxnCurrency.Contains(s.Currency))
.Where(s => TerminalID == null || TerminalID.Contains(s.TerminalID))
.Where(s => TxnAmount.ToString() == null || TxnAmount==(s.TransactionAmount))
.Where(s => BIN == null || BIN.Contains(s.Bin))
.Where(s => MsgType == null || MsgType.Contains(s.MessageType))
.Where(s => MaskedPan == null || MaskedPan.Contains(s.PANM))
.Where(s => ProcessingCode == null || ProcessingCode.Contains(s.ProcessingCode))
.Where(s => ClearPan == null || ClearPan.Contains(s.PAN))
.Where(s => ResponseCode == null || ResponseCode.Contains(s.ResponseCode))
.Where(s => AuthorizationCode == null || AuthorizationCode.Contains(s.AuthorizationCode))
.Where(s => EntryMode == null || EntryMode.Contains(s.PosEntryMode))
.AsQueryable();
Unfortunately it does not work correctly. Can someone tell me what I am missing or if there is a better way to write this?
Took advice from the comments and went through each line and found which line was evaluating false. This fixed my problem.
I think the best you can do there is check first if you should apply the condition and then filter the list.
An example using the code you provided.
var queryable = context.TransactionJournal.Where(s => s.TransactionDateTime <= transactionDate);
if (!string.IsNullOrEmpty(your_objet.Region)
{
var queryable = queryable.Where(x=>x.Region == your_objet.Region).AsQueryable();
}
if (!string.IsNullOrEmpty(your_objet.MCC)
{
var queryable = queryable.Where(x=>x.MCC == your_objet.MCC).AsQueryable();
}
The first line is the entire list, then you check all parameters that you have in the form and evaluate it, if has value the apply the filter to list.
And the end you'll get your list filtered.

EF Core Exclude Where Multiple Columns are Null or Empty

I have a SQL Server table called ControlActivityChangeLog. The table has columns CurrentValue and NewValue, of which, both are nullable nvarchars. How do I query all ControlActivityChangeLogs but exclude results where CurrentValue and NewValue are null or empty. If CurrentValue is null or empty and NewValue is not then I need that record.
I tried adding this to the below query but it ends up returning no results:
!x.ControlActivityChangeLogs.Any(y => string.IsNullOrEmpty(y.CurrentValue) && string.IsNullOrEmpty(y.NewValue))
Here is my qry:
var qry = _context.ControlActivities
.Include(x => x.ControlActivityChangeLogs)
.Include(x => x.Company)
.Where(x =>
!x.IsDeleted &&
!x.IsArchived &&
!x.ControlActivityChangeLogs.Any(y => string.IsNullOrEmpty(y.CurrentValue) && string.IsNullOrEmpty(y.NewValue)))
.AsQueryable();
**OTHER COLUMN FILTERS HERE**
var results = await qry.Select(x => new ModifiedCAModel
{
**List Of Columns**,
Changes = x.ControlActivityChangeLogs
.Where(y => y => y.ControlActivityIssueId == null && y.ControlActivityTestId == null)
.OrderByDescending(y => y.ChangedDate)
.ToList()
}).ToListAsync();
The Where or Any condition is for including, so you need the inverse of
exclude results where CurrentValue and NewValue are null or empty
which is
include results where CurrentValue or NewValue is not null or empty
i.e. instead of
!x.ControlActivityChangeLogs.Any(y => string.IsNullOrEmpty(y.CurrentValue) && string.IsNullOrEmpty(y.NewValue)))
you need
x.ControlActivityChangeLogs.Any(y => !string.IsNullOrEmpty(y.CurrentValue) || !string.IsNullOrEmpty(y.NewValue)))
the below should be able to translate to SQL correctly
var qry = _(context.ControlActivities
.Include(x => x.ControlActivityChangeLogs)
.Include(x => x.Company)
.Where(x => !x.IsDeleted
&& !x.IsArchived
&& x.ControlActivityChangeLogs.CurrentValue != null
&& x.ControlActivityChangeLogs.CurrentValue != ""
&& x.ControlActivityChangeLogs.NewValue != null
&& x.ControlActivityChangeLogs.NewValue != ""
).AsQueryable();
you should check what sql is being executed tho!!!
this can be obtained but adding logging to ef.

If/else selected count from sql database

What I'm doing now is getting my SQL database table: IsAcross varchar(45) into my if/else statement. My IsAcross table only consist of YES and NO.
So now I want to take out only select YES statement from SQL Server, so therefore I have put the thingy in the whole list first. Then I use if/else statement to extract out the YES but how am I supposed to do that?
Example: I have a total of 7 items in a list, 4 yes 3 no. I want to take out the all the 4 yes only. Something like that:
ViewModels.WordSearchVM wsvm = new ViewModels.WordSearchVM();
wsvm.ActivityID = id;
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Select(m => m.IsAcross)
.AsEnumerable()
.ToList();
if (results = "yes")
{
else
}
As far as I understand your problem, you can forget your if statement and simply extend the .Where part:
.Where(m => m.ActivityID == id && m.results=="yes")
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Where(m => m.IsAcross)
.AsEnumerable()
.ToList();
//OR
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Where(m => m.IsAcross == "YES")
.AsEnumerable()
.ToList();
var results = db.CrossPuzzles
.Where(m => m.ActivityID == id)
.Select(m => m.IsAcross)
.Where(x => x == "YES") // filter to "YES"
.ToList();
if (results.Count > 0)
// YES
else
// NO
This what you're looking for?

Lambda Expression to search with conditions and return only 1 latest result

I have a List that contains Supplier data and I would like to search it by using SupplierID, non-active supplier and only 1 latest result.
So I've got:
List<Supplier> filteredList = this.toList();
filteredList.OrderByDescending(m => m.ModifiedDatetime).FirstOrDefault();
filteredList.Where(f => (f.Active == false && f.FieldId == SupplierFieldID))
.ToList<Supplier>();
But I can't make this work; please help.
You need to chain your LINQ expressions, like this:
var filteredList = unfilteredData
.Where(f => f.Active == false && f.FieldId == SupplierFieldID)
.OrderByDescending(m => m.ModifiedDatetime)
.FirstOrDefault();
You do not need a ToList(), because you need a single item, not a list; this is what FirstOrDefault() does. If you need the last item, you need to order by the reverse of your original ordering condition. For example, if you would like the entry with the latest modified date, you need to order by descending (as you did).
You can do this in one statement, chaining together the LINQ operators:
var filteredList = myList.Where(f => f.Active == false && f.FieldId == SupplierFieldID)
.OrderByDescending(m => m.ModifiedDatetime)
.Take(1);
or as #Preston Guillot suggested, the even shorter form:
var filteredList = unfilteredData
.OrderByDescending(m => m.ModifiedDatetime)
.FirstOrDefault(f => f.Active == false && f.FieldId == SupplierFieldID);

Categories