Not supported exception with TruncateTime entity framework function - c#

I'm getting an exception when trying to use TruncateTime() in my linq expresion
and I'm not sure why?
Here is my statement
var yogaProfile = dbContext.YogaProfiles.Where(i => i.ApplicationUserId == userId).First();
var yogaEvents = yogaProfile.RegisteredEvents.Where(j =>
(j.EventStatus == YogaSpaceEventStatus.Active || j.EventStatus == YogaSpaceEventStatus.Completed)
&& DbFunctions.TruncateTime(j.UTCEventDateTime) > DbFunctions.TruncateTime(yesterday)
&& DbFunctions.TruncateTime(j.UTCEventDateTime) <= DbFunctions.TruncateTime(todayPlus30)
).ToList();
and here is the exception
System.NotSupportedException: This function can only be invoked from LINQ to Entities.
at System.Data.Entity.DbFunctions.TruncateTime(Nullable`1
dateValue)
at
YogaBandy2017.Services.Services.YogaSpaceService.<>c__DisplayClass9_1.b__1(YogaSpaceEvent
j) in
C:\Users\chuckdawit\Source\Workspaces\YogaBandy2017\YogaBandy2017\Yogabandy2017.Services\Services\YogaSpaceService.cs:line
256
at System.Linq.Enumerable.WhereListIterator`1.MoveNext()
at System.Collections.Generic.List'1..ctor(IEnumerable`1
collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at
YogaBandy2017.Services.Services.YogaSpaceService.GetUpcomingAttendEventCounts(String
userId) in
C:\Users\chuckdawit\Source\Workspaces\YogaBandy2017\YogaBandy2017\Yogabandy2017.Services\Services\YogaSpaceService.cs:line
255
at
YogaBandy2017.Controllers.ScheduleController.GetEventCountsForAttendCalendar()
in
C:\Users\chuckdawit\Source\Workspaces\YogaBandy2017\YogaBandy2017\YogaBandy2017\Controllers\ScheduleController.cs:line
309

System.NotSupportedException: This function can only be invoked from LINQ to Entities.
You have this exception because the result of yogaProfile variable is not an IQueryable which is used by Linq To Entities to perform query on server side. To make your variable to be an IQueryable you need to remove the extension method First() you use on the query at the end and replace it with Take(1).
So instead of
var yogaProfile = dbContext.YogaProfiles.Where(i => i.ApplicationUserId == userId).First();
You should have this:
var yogaProfile = dbContext.YogaProfiles.Where(i => i.ApplicationUserId == userId).Take(1);
Note that with the first statement you are dealing with Linq To Objects when you perform Linq on the variable. By removing First() extension method and replace it with Take() extension method you'll perform Linq To Entites.

Related

How to pass func expression in LINQ where clause?

This is my Custom filter(Func) to pass in where clause
Func<Project,bool> filter = f =>
{
bool filteredContent = true;
if (!CreatorId.Equals(0))
filteredContent = f.CreatedBy.Equals(CreatorId);
if (filteredContent && !VerticalMarketId.Equals(0))
filteredContent = f.VerticalMarketsId.Equals(VerticalMarketId);
if (filteredContent && !ProductCategoryId.Equals(0))
filteredContent = f.ProductCategoriesId.Equals(ProductCategoryId);
return filteredContent;
};
This is my code where I get all the projects based on the conditions created in filter expression
getProjects = await _context.Projects.Where(x => x.IsDeleted == false && filter.Invoke(x))// Here I'm getting the exception
.Include(PC => PC.ProjectComments.Where(x => x.IsDeleted == false))
.Include(SP => SP.SharedProjects)
.AsNoTracking().ToListAsync();
Exception:The LINQ expression (DbSet......) 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'.
Can someone tell how can I filter the data using expression in this?
NOTE: I can do ToListAsync() before applying the filter, but it'll get all the records from DB then filter on client side. But I want to filter the data on server side.
IF you were using Linq To Objects that should work but you are doing Linq To SQL and in this case you must think on how you would translate this function into a valid SQL statement. Question yourself: How could I pass this function call in a SQL Statement? Depending what you do on the body of your expression, you cannot translate it to SQL, you must be simpler sometimes.
Candidate solution
Add PredicateBuilder class on your project. It will give you easily logical operators to you handle expressions.
http://www.albahari.com/nutshell/predicatebuilder.aspx
Try to define an expression and pass it as argument on Where method of your query method chain. For sample (read the comments):
// define a expression with default condition
Expression<Func<Project, bool>> filter = f => !f.IsDeleted;
// check conditions to add new filtes with `And` logical operator
if (!CreatorId.Equals(0))
filter = filter.And(f => f.CreatedBy.Equals(CreatorId));
else if (!VerticalMarketId.Equals(0))
filter = filter.And(f => f.VerticalMarketsId.Equals(VerticalMarketId));
else if (!ProductCategoryId.Equals(0))
filter = filter.And(f => f.ProductCategoriesId.Equals(ProductCategoryId));
// apply the filter on the query and execute it
getProjects = await _context.Projects.Where(filter)
.Include(PC => PC.ProjectComments.Where(x => !x.IsDeleted))
.Include(SP => SP.SharedProjects)
.AsNoTracking()
.ToListAsync();
Note: I didn't test this code and it probably should be fixed somehow!
Important tips on Linq To SQL:
Logical operators are ok and tend to be translated fine to sql;
Where(x => x.Children.Any(j => j.Children.Any())), each Any call generates a subquery on query scope, be careful with it given it can compromise your database performance.
If you just need to check the existence of an item, use queryable.Any(expression).
If you need to check and then do something, prefer using queryable.FirstOrDefault(expression) and check if the result is null before using.
Use paging with .Take(int) and .Skip(int).
Always concrete your queries by calling .ToList(), .ToArray() or async versions of these methods. Avoid passing queryable in the top layers (query can be executed out of the scope you want).
I figured it out by creating a simple Expression as fololows:
private static Expression<Func<Project, bool>> ProjectFilterExpression(
int creatorId,
int verticalMarketId,
int productCategoryId)
{
Expression<Func<Project, bool>> projectFilterExpression = pfe =>
!pfe.IsDeleted
//CreatorId Filter
&& (creatorId.Equals(0) || pfe.CreatedBy.Equals(creatorId))
//Vertical Market Filter
&& (verticalMarketId.Equals(0) || pfe.VerticalMarketsId.Equals(verticalMarketId))
// Product Category Filter
&& (productCategoryId.Equals(0) || pfe.ProductCategoriesId.Equals(productCategoryId));
return projectFilterExpression;
}
Then I call this static method inside my filter method.
var filter = ProjectFilterExpression(CreatorId, VerticalMarketId, ProductCategoryId);
And finally I applied this filter in my LINQ where clause
getProjects = await _context.Projects.Where(filter).AsNoTracking().ToListAsync();
It's working totally fine.

How can I query a database with LINQ when the column is a number but I have a string in the WHERE?

I am using this code:
query = String.IsNullOrEmpty(options.PhraseNum) ?
query :
query.Where(w => w.PhraseNum == Convert.ToInt32(options.PhraseNum));
However I get an error:
LINQ to Entities does not recognize the method 'Int32 ToInt32(System.String)' method, and this method cannot be translated into a store expression.
Is there a way I can do this in LINQ and if not how can I convert outside of this and have the conversion not cause an exception if the string is not null?
Only modify query if the string can be parsed to an int, which is an implicit check if it isn't null or empty:
if (int.TryParse(options.PhraseNum, out var phraseNum))
{
query = query.Where(w => w.PhraseNum == phraseNum);
}
Before C# 7 the syntax was
int phraseNum;
if (int.TryParse(options.PhraseNum, out phraseNum))
etc.
It looks like LINQ is trying to evaluate the expression Convert.ToInt32(options.PhraseNum) on the server side as part of the WHERE clause.
Redo the code so that the variable is cast explicitly on the client-side outside the query expression:
Int32 phrase_num = String.IsNullOrEmpty(options.PhraseNum) ? 0 : Convert.ToInt32;
query = String.IsNullOrEmpty(options.PhraseNum) ? query : query.Where(w => w.PhraseNum == phrase_num);
Or for a tidier approach overall:
if(!String.IsNullOrEmpty(options.PhraseNum))
{
Int32 phrase_num = Convert.ToInt32(options.PhraseNum);
query = query.Where(w => w.PhraseNum == phrase_num);
}
I think that should resolve the problem and preserve the intended program logic.
EDIT: I'd endorse Gert Arnold's approach above if you are using the latest C# version, where out parameters can be declared inline in the TryParse method call itself.

LINQ to Entities does not recognize the method 'System.String ToString(Int32)'

Hi I am using a linq query which is throwing the error LINQ to Entities does not recognize the method 'System.String ToString(Int32)' method, and this method cannot be translated into a store expression.
List<string> resultMap = (from item in mapResult
select Convert.ToString(item.ResultDE)).ToList();
Error is throwing in this below statement
List<Result_DE> resultList = (from result in db.Result_DE
where result.IsActive == "1"
&& resultMap.Contains(Convert.ToString(Convert.ToInt32(result.ID)))
select result).ToList();
please tell me the proper way of writing this query.
You cannot use these conversion functions in a LINQ to Entities statement, they cannot be translated to SQL, you need to do the conversions in memory. But I don't think you need to do that at all.
If you were just using the resultMap to get your resultList, filtered by Results of which the Id is present in mapResult, do the following:
var resultList = db.Result_DE
.Where(r => r.IsActive == "1" && mapResult.Any(mr => mr.ResultDE == r.ID));
.ToList();
If mapResult is an in-memory collection, instead of an IQueryable that is attached to the db context, you need to do the following:
var resultIds = mapResult.Select(mr => mr.ResultDE).ToList();
var resultList = db.Result_DE
.Where(r => r.IsActive == "1" && resultIds.Contains(r.ID));
.ToList();
Before you call any method (e.g. ToString()), you need to convert LINQ to Object using AsEnumerable().
if your item.ResultDE and result.ID is variable type of Int32,
why don directly create a List<Int32> ?
List<Int32> resultMap = (from item in mapResult
select item.ResultDE).ToList<Int32>();
List<Result_DE> resultList = (from result in db.Result_DE
where result.IsActive == "1"
&& resultMap.Contains(result.ID)
select result).ToList<Result_DE>();
Use SqlFunctions.StringConvert instead of Convert.ToString.
A similar question was asked and answered here

Local sequence cannot be used in LINQ to SQL

Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.
I am getting this error from the below linq query:
List<Something> results = new List<Something>(items
.Where(w => selecteditems.Count == 0 || w.ops.Intersect(selecteditems).Count() > 0)
.ToList()
.OrderBy(a => a.FirstNumber)
.OrderBy(b => b.SecondNumber));
Would the intersect be throwing this error?
The query provider doesn't know how to translate w.ops.Intersect(selecteditems) into a SQL query.
If selecteditems was another query from the same query provider then it may be able to translate them, or if the entire operation were being done in Linq-to-Objects, rather than Linq-to-SQL, then it would be fine.
As per the error message, the only operation it knows how to perform on such an object is Contains. You can re-work your query to use that instead:
.Where(w => selecteditems.Count == 0 ||
w.ops.Any(op => selecteditems.Contains(op)))
That [should] work.

Entity Framework ToString method

Following code block throws error.
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
db.tbOnIgmHawbDetails
.Where(s => !db.tbImpoExaminations.Any(x => x.Hawb.ToString() == s.Hawb) && s.AwbNo == p)
.Select(s => s.Hawb).ToList();
Any suggestion? why this happen and what is the solution?
.ToString() is supported properly going forward with EF 6.1: http://blogs.msdn.com/b/adonet/archive/2014/03/17/ef6-1-0-rtm-available.aspx
You could try with SqlFunctions.StringConvert... Use the decimal conversion:
SqlFunctions.StringConvert((decimal)p.x.Hawb).TrimLeft() == ...
(the TrimLeft is necessary because the STR function of SQL will right align the number)
If s.Hawb is already string type (the error message suggests so), then remove the part .ToString() from your query.
The reason for it is that in LINQ2SQL, you can only use those language constructs that can be translated into SQL. For example, if you try to use RegEx in your C# expression, then SQL does not have a corresponding construct for RegEx, and thus LINQ cannot translate and execute your query.
Easily add .AsEnumerable() before the .ToString() and those methods that L2E doesn't support:
var asen = db.tbOnIgmHawbDetails.AsEnumerable();
var result = asen.Where(s => !asen.Any(x => x.Hawb.ToString() == s.Hawb) && s.AwbNo == p)
.Select(s => s.Hawb).ToList();
That should works. However if not, try to perform your query by linq-to-objects syntax:
var result = from a in asen
where ...
select ...;
do not use ToString
just use like
x.Hawb + "" == s.Hawb

Categories