using where clause after get last record? - c#

using (EPOSEntities1 db = new EPOSEntities1())
{
List<ActionPerformed> PLUlist = db.ActionPerformeds.ToList();
ActionPerformed Latest_PLU = PLUlist.OrderByDescending(x => x.Date).FirstOrDefault();
}
This returns the last record stored. However I have now added another column in the table File_Name, how can I add a where clause to this to say orderByDescending to get the latest file, then from there get the first record with the file_Name as 'Sales'.??
so e.g.
File_Name Date
12) Products 11/02/2014
13) Sales 11/02/2014
14) Products 11/02/2014
this would return record 13??

The Where method can filter your collection to only those items where the File_Name is "Sales".
Consider placing your LINQ query before the executing call so that your LINQ-to-DB provider can perform the query server-side and only return you one item. What you were doing is bringing the entire ActionPeformeds table down from the server to the client, then performing the query client-side.
ActionPerformed Latest_PLU = db.ActionPerformeds
.Where(x => x.File_Name == "Sales")
.OrderByDescending(x => x.Date)
.FirstOrDefault();
By "executing call" I mean ToList(), First(), FirstOrDefault(), etc.

Use Where clause before OrderByDescending, if you call ToList it will cause immediate evaluation of the query and records will populated. It will be better if you call Where before evaluation.
ActionPerformed Latest_PLU = db.ActionPerformeds.Where(c=>File_Name == "Sales")
.OrderByDescending(x => x.Date)
.FirstOrDefault();

ActionPerformed Latest_PLU = PLUlist.Where(p => p.File_Name == 'Sales').OrderByDescending(x => x.Date).FirstOrDefault();

Related

select items/rows after a certain Id

I have a list of items (linq to sql), which can be ordered by various properties, not just by Id asc/desc
If the list was ordered only by Id,
I could call list.Where(o => o.Id > 123).Take(10)
is there any linq methods/functionality that would allow to ?
Take 10 items after id with value = 123
SQL has no concept of row ordering so it you can't request after a particular row from SQL. However, LINQ to Objects does so you can use that:
If list is a List<T> created from your query, just use SkipWhile:
list.SkipWhile(o => o.Id != 123).Skip(1).Take(10)
If list is a LINQ to SQL Query, then first use AsEnumerable but realize this will bring over the entire query to the client:
list.AsEnumerable().SkipWhile(o => i.Id != 123).Skip(1).Take(10)
If you know the ordering and can test against it, you could filter and reduce the amount of data returned to the client by 1. Computing the maximum duplicates of the ordering for a given Id and 2. Only returning the records in the desired order that are greater than or equal to the sort value(s) for the desired Id:
var maxdup = list.GroupBy(o => o.someProp).Select(og => og.Count()).Max();
var ans = list.Orderby(o => o.someProp)
.Where(o => o.someProp >= list.Where(o2 => o2.Id == 123).First().someProp)
.Take(10+maxdup)
.AsEnumerable()
.SkipWhile(o => o.Id != 123).Skip(1)
.Take(10);

Order by user and then select max date

I have this LINQ query:
ArrayList arr = new ArrayList();
var data = conn.SCOT_DADOS.OrderByDescending(x => x.DATE)
.GroupBy(r => r.USER)
.ToList();
foreach (var item in data)
{
var itemdata = item.Where(r => r.DATE == item.Max(s => s.DATE));
var name = svc.GetUserName(itemdata.Select(r => r.USER).First().ToString());
var value = itemdata.Select(r => r.VALUE).First();
var date = itemdata.Select(r => r.DATE).First().ToString("dd/MM/yyyy HH:mm:ss");
arr.Add( new{ NAME = name, DATE = date, VALUE = value} );
}
This code will give me the latest result by DATE for each USER.
But the LINQ query is selecting all data from the user and then I'm getting the latest one in the foreach loop.
Is there any way to get only the last data in the LINQ query, so I don't have to take all the user data every time?
I have tried this:
var data = conn.SCOT_DADOS.OrderByDescending(x => x.DATE)
.GroupBy(r => r.USER)
.First()
.ToList();
And then treated item as an object, instead of running selects on it.
It gave me all the data for an individual user, which isn't what I want.
What can be done?
Edit 1:
I get this error if I try to swap OrderByDescending and GroupBy:
Error CS1061 'IGrouping' does not contain a
definition for 'DATE' and no extension method 'DATE' accepting a first
argument of type 'IGrouping' could be found (are
you missing a using directive or an assembly reference?)
Edit 2:
This is some sample data (the column names are not the same because I translated them for the question):
From the data presented, I'd have the results:
If the combination of the (USER, DATE) pair is unique (which seems to be the case when looking at the sample data), the requirement can be trimmed down to
return each record if there is no other record with the same USER and later DATE
which could be translated to the following LINQ query:
var result = conn.SCOT_DADOS
.Where(r => !conn.SCOT_DADOS.Any(r2 => r2.USER == r.USER && r2.Date > r.Date))
// end of Db Query
.AsEnumerable()
.Select(r => new
{
Name = svc.GetUserName(r.User),
Value = r.Value,
Date = r.Date.ToString("dd/MM/yyyy HH:mm:ss")
}).ToList();
I'm a bit confused but from your attempts with First() think you mean this:
conn.SCOT_DADOS.GroupBy(item => item.User)
.Select(grp => grp.OrderByDescending(i => t.Date).First());
This will retrieve for each User only the latest record of it
The reason only swapping the GroupBy and OrderByDescending isn't enough and that you need the Select is that once you grouped that data your enumerable is IEnumerable<IGrouping<User,YourType>>. Each IGrouping is actually a collection by itself so you need to Select only the 1 item you want from it.
Another way is to replace the Select with:
.SelectMany(grp => grp.OrderByDescending(i => t.Date).Take(1))
IMO the first is cleaner, but the second is in the case you need for each user N first items
On the query above you can also add what you have in the foreach loop:
conn.SCOT_DADOS.GroupBy(item => item.User)
.Select(grp => grp.OrderByDescending(i => t.Date).First())
.AsEnumerable()
.Select(item => new {
Name = svc.GetUserName(item.User),
Value = item.Value,
Date = item.Date.ToString("dd/MM/yyyy HH:mm:ss")
}).ToList();
The use of the AsEnumerable() is to invoke the query to be executed to the database before the last Select() which uses the GetUserName method that will not be known to the Oracle database
IMO representing the DateTime as string is not a good way..
Update - The error you get:
Oracle 11.2.0.3.0 does not support apply
It seems that as for this version of Oracle it does not support GroupBy with Select via linq. See Linq to Entities Group By (OUTER APPLY) “oracle 11.2.0.3.0 does not support apply”.
One answer there recommended to create a view in the database for this and then use linq to select over that view. That is what I'd go for
Try this
conn.SCOT_DADOS.GroupBy(x => x.User).Select(x => new
{
User = x.Key,
Date = list.Where(y => y.User == x.Key).Max(y => y.Date)
});

LINQ Query find columns where string contains any letter

I'm trying to find all customer codes where the customer has a status of "A" and whose code does not contain any letter using LINQ query.
var activeCustomers = Customers.Where(x => x.Status == "A" && x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
When I run this query in LinqPad I get the following error:
You'll need to do this as a two part query. First, you could get all the users who's status is "A":
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
After you've got those in-memory, you can create an additional filter for char.IsDigit:
var codes = activeCustomers.Where(x => x.Code.Any(n => !char.IsLetter(n)))
.Select(x => x.Code)
.ToArray();
As commenters have stated, IsLetter() cannot be translated to SQL. However, you could do the following, which will first retrieve all items with Status "A" from the database, then will apply your criteria after retrieval:
var activeCustomers = Customers.Where(x => x.Status == "A").AsEnumerable().Where(x => x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
You'll have to determine if it's acceptable (from a performance perspective) to retrieve all customers with "A" and then process.
The AsEnumerable() transitions your LINQ query to working not with IQueryable (which works with SQL) but with IEnumerable, which is used for plain LINQ to objects.
Since it is LINQ 2 SQL, there is no natural way to translate char.IsLetter to something SQL can understand. You can hydrate a query that retrieves your potential candidates and then apply an addition in-memory filter. This also solves the issue where LINQ 2 SQL has a preference for a string and you are dealing with chars
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
var filteredCustomers = activeCustomers.Where(x =>
x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code).ToList();
There are two performance hits here. First, you're retrieving all potential records, which isn't too desirable. Second, in your above code you were only interested in an enumerable collection of codes, which means our query is including far more data than we originally wanted.
You could tighten up the query by only returning back to columns necessary to apply your filtering:
var activeCustomers = Customers.Where(x => x.Status == "A")
Select(x => new Customer{ Status = x.Status, Code = x.Code }).ToList();
You still return more sets than you need, but your query includes fewer columns.

Linq select with filtering not working

I'm trying to select one field last record in filtered database (this is different than last inserted record). I tried with following code in controller but instead of field value, i'm getting "true" or "false", depending on if there's results after filtering or not.
List<Pozicije> poz = new List<Pozicije>();
poz = db.Pozicijes.Where(p => p.grupa == grupa)
.OrderBy(p => p.sifra_pozicije).ToList();
string pos = poz.Select(p => p.sifra_pozicije.Contains(s)).LastOrDefault().ToString();
can someone point me how to get value i need instead?
Try this instead. I've combined both parts of your query into one.
var pos =
Convert.ToString(db.Pozicijes.Where(p => p.grupa == grupa
&& p.sifra_pozicije.Contains(s))
.OrderByDescending(p => p.sifra_pozicije)
.Select(p => p.sifra_pozicije)
.FirstOrDefault());
If it doesn't work, you may need to tell us what types s and sifra_pozicije are.
LastOrDefault is not supported with LINQ to Entities/LINQ TO SQL. You need to do OrderByDescending and then get First record. Like:
string pos = db.Pozicijes.Where(p => p.grupa == grupa && p.sifra_pozicije.Contains(s)))
.OrderByDescending(p=> p.sifra_pozicije)
.Select(r=> r.sifra_pozicije)
.First();

Linq Contains in one query

I have a List and i want to write a query about List's ids Contains specific table id.
i Write this and running true but i want to write all in same query..
List<int> tempList=yetkiUygulamaList.Select(y => y.Id).ToList();
query = query.Where(x => tempList.Contains(x.Uygulama.Id));
Wrong Query
query = query.Where(x => yetkiUygulamaList.Select(y =>y.Id).ToList().Contains(x.Uygulama.Id));
this must works
query = query.Where(x => yetkiUygulamaList.Any(y=>y.Id == x.Uygulama.Id));
you can perform a join, it would be more simple and suitable in your case.
If I understand, query is a "collection" of a class (let's call it AObj) containing a property Uygulama and the class Uygulama contains a property Id and yetkiUygulamaList is a "collection" of Uygulama
//will return a IEnumerable<AObj>
IEnumerable<AObj> query = query.Join(yetkiUygulamaList, a => a.Uygulama.Id, u => u.Id, (a,u)=>a);
ToList() materilizes by executing the query, and after that there is no way for NHibernate to understand that the first query should be included as a subquery.
Just remove the useless ToList():
IQueryable<int> tempList = yetkiUygulamaList.Select(y => y.Id); // removed here
query = query.Where(x => tempList.Contains(x.Uygulama.Id));
The above code will generate a single SQL query. If you want to stick it all in one C# code line, just get rid of the intermediary variable:
query = query.Where(x => yetkiUygulamaList.Select(y => y.Id).Contains(x.Uygulama.Id));

Categories