I am trying to query my Database for a single record ins three steps and I am having problems getting the result that I am looking for. These are the steps that I created:
client = client
.Where(s => s.CompanyName.Contains(name));
var res = client.Select(x => x.ID);
Tracker = Tracker
.Where(s => s.ClientId.Equals(client.Select(x => x.ID)));
Debugging the code indicated that steps one and two worked correctly and generated the data that I needed to run my third query, which should provide the whole record, utilizing the result of the second step.
The third and last steps generated the following error:
"The LINQ expression DbSet<TimeTrackerViewModel>().Where(t => t.ClientId.Equals(DbSet<ClientsViewModel>().Where(c => c.CompanyName.Contains(__name_0)).Select(c => c.ID))) could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information."
How do I query my database, utilizing the query result from the second step?
If you want to use the IDs in res to filter Tracker then you can use Any():
var res = client.Select(x => x.ID);
trackers = Tracker.Where(s => res.Any(r => r == s.ClientId));
The above query will return a collection.
I am trying to query my Database for a single record
If you want to return a single record then you could to use FirstOrDefault(), either in place of the Where clause (using the same predicate), or after the Where (you could consider Single() if you know there's exactly 1 matching record). But you should also consider what you expect to happen if multiple records match the name parameter in your first query and how you would handle that.
You should try this:
var trakers = (from c in client.Where(s => s.CompanyName.Contains(name))
join t in tracker
on c.ID
equals t.ClientId
select t).ToList();
So you do only a query on db.
After reading several related posts, I was able to combine thier ideas into a single working solution as posted below:
var client = _context.ClientsViewModel
.Where(s => s.CompanyName.Contains(name))
.Select(x => x.ID).ToList();
Tracker = Tracker
.Where(s => s.ClientId == client[0])
.OrderByDescending(x => x.Id);
Related
I'm having trouble with a LINQ query.
var cRecords = context.ClassificationRecords
.GroupBy(p => p.ImageRecordId)
.Select(g => g.OrderByDescending(c => c.Created).FirstOrDefault())
.ToList();
What I'm trying to do is get a list of the last dated classification record for each user. I'll include the user and some other stuff, but that's the stripped down version.
The problem is that the above is giving me an error:
System.InvalidOperationException: 'The LINQ expression '(GroupByShaperExpression:
KeySelector: (c.ImageRecordId),
ElementSelector:(EntityShaperExpression:
EntityType: ClassificationRecord
ValueBufferExpression:
(ProjectionBindingExpression: EmptyProjectionMember)
IsNullable: False
)
)
.OrderByDescending(c => c.Created)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
I've seen this query used in several places on here and other websites, so it should be working..?
I'm using .Net Core 3.1 with EntityFrameworkCore 3.1.11
The query is being ran inside a hangfire worker program, so not a Blazor server - not sure if it makes a difference.
One possible work around is to modify your query into something EF 3.1 can translate - how efficient this will be depends on your database engine. I think this should work but you need the primary key to match the records:
var cRecords = context.ClassificationRecords
.Where(c => c.PrimaryKey == context.ClassificationRecords.Where(c2 => c2.ImageRecordId == c.ImageRecordId)
.OrderByDescending(c2 => c2.Created)
.First().PrimaryKey)
.ToList();
This query is not possible with EFC. After GroupBy you can select only grouping key or aggregation result. Faster query here is Window Functions usage which are not supported by EF.
So there is workaround:
var grouped =
from c in ontext.ClassificationRecords
group c by c.ImageRecordId into g
select new
{
ImageRecordId = g.Key,
Created = g.Max(x => x.Created)
};
var query =
from g in grouped
join c in context.ClassificationRecords
on new { g.Created, g.ImageRecordId } equals { c.Created, c.ImageRecordId }
select c;
var result = query.ToList();
In this query:
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderBy(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.Last());
}
I had to switch it to this for it to work
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName)
.Select(p => p.FirstOrDefault());
}
I couldn't even use p.First(), to mirror the first query.
Why are there such basic limitations in what's otherwise such a robust ORM system?
That limitation comes down to the fact that eventually it has to translate that query to SQL and SQL has a SELECT TOP (in T-SQL) but not a SELECT BOTTOM (no such thing).
There is an easy way around it though, just order descending and then do a First(), which is what you did.
EDIT:
Other providers will possibly have different implementations of SELECT TOP 1, on Oracle it would probably be something more like WHERE ROWNUM = 1
EDIT:
Another less efficient alternative - I DO NOT recommend this! - is to call .ToList() on your data before .Last(), which will immediately execute the LINQ To Entities Expression that has been built up to that point, and then your .Last() will work, because at that point the .Last() is effectively executed in the context of a LINQ to Objects Expression instead. (And as you pointed out, it could bring back thousands of records and waste loads of CPU materialising objects that will never get used)
Again, I would not recommend doing this second, but it does help illustrate the difference between where and when the LINQ expression is executed.
Instead of Last(), Try this:
model.OrderByDescending(o => o.Id).FirstOrDefault();
Replace Last() by a Linq selector OrderByDescending(x => x.ID).Take(1).Single()
Something like that would be works if you prefert do it in Linq :
public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
var context = DataContext.GetDataContext();
return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}
Yet another way get last element without OrderByDescending and load all entities:
dbSet
.Where(f => f.Id == dbSet.Max(f2 => f2.Id))
.FirstOrDefault();
That's because LINQ to Entities (and databases in general) does not support all the LINQ methods (see here for details: http://msdn.microsoft.com/en-us/library/bb738550.aspx)
What you need here is to order your data in such a way that the "last" record becomes "first" and then you can use FirstOrDefault. Note that databasese usually don't have such concepts as "first" and "last", it's not like the most recently inserted record will be "last" in the table.
This method can solve your problem
db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();
Adding a single function AsEnumerable() before Select function worked for me.
Example:
return context.ServerOnlineCharacters
.OrderByDescending(p => p.ServerStatus.ServerDateTime)
.GroupBy(p => p.RawName).AsEnumerable()
.Select(p => p.FirstOrDefault());
Ref:
https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys
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.
In orchard cms i want to write a query containing two condition over two different ContentPartRecord combined with OR clause. one possible way to combine conditions is :
var query = _contentManager.HqlQuery()
.Where(a => a.ContentPartRecord<ProductPartRecord>(), a => a.Eq("Title", "myTitle"))
.Where(a => a.ContentPartRecord<TitlePartRecord>(), a => a.Eq("Price", 1000))
.List();
but this one combines two condition with And clause. i think something like following along with some changes (which would be the answer for this question) could be the case:
var query = _contentManager.HqlQuery()
.Join(a => a.ContentPartRecord<ProductPartRecord>())
.Join(a => a.ContentPartRecord<TitlePartRecord>())
.Where(a => a.ContentItem(),
a => a.Or(p => p.Eq("ProductPartRecord.Price",
"1000"), t => t.Eq("TitlePartRecord.Title", "myTitle")))
.List();
but i couldn't get it working.any body have any suggestion?
When you use where clause in HqlQuery you need to pass Alias (as first parameter). Alias means that you will be apply where clause to specific table (class that represent this table). And when you use OR clause that you definitely need to compare columns of the specific table in two parts of OR clause (left and right from the OR clause). And i think this is not possible to do you need a standard way.
But you can use:
Action<IAliasFactory> productPartRecordAlias = x => x.ContentPartRecord<ProductPartRecord>().Named("productPartRecord");
Action<IAliasFactory> titlePartRecordAlias = x => x.ContentPartRecord<TitlePartRecord>().Named("titlePartRecord");
var query = _contentManager.HqlQuery()
.Join(productPartRecordAlias)
.Join(titlePartRecordAlias)
.Where(a => a.ContentItem(), p => p.Gt("Id", "0 AND (productPartRecord.Price = 1000 OR titlePartRecord.Title = 'myTitle')"));
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));