Why does this LINQ-to-SQL query get a NotSupportedException? - c#

The following LINQ statement:
public override List<Item> SearchListWithSearchPhrase(string searchPhrase)
{
List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase);
using (var db = Datasource.GetContext())
{
return (from t in db.Tasks
where searchTerms.All(term =>
t.Title.ToUpper().Contains(term.ToUpper()) &&
t.Description.ToUpper().Contains(term.ToUpper()))
select t).Cast<Item>().ToList();
}
}
gives me this error:
System.NotSupportedException: Local
sequence cannot be used in LINQ to SQL
implementation of query operators
except the Contains() operator.
Looking around it seems my only option is to get all my items first into a generic List, then do a LINQ query on that.
Or is there a clever way to rephrase the above LINQ-to-SQL statement to avoid the error?
ANSWER:
Thanks Randy, your idea helped me to build the following solution. It is not elegant but it solves the problem and since this will be code generated, I can handle up to e.g. 20 search terms without any extra work:
public override List<Item> SearchListWithSearchPhrase(string searchPhrase)
{
List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase);
using (var db = Datasource.GetContext())
{
switch (searchTerms.Count())
{
case 1:
return (db.Tasks
.Where(t =>
t.Title.Contains(searchTerms[0])
|| t.Description.Contains(searchTerms[0])
)
.Select(t => t)).Cast<Item>().ToList();
case 2:
return (db.Tasks
.Where(t =>
(t.Title.Contains(searchTerms[0])
|| t.Description.Contains(searchTerms[0]))
&&
(t.Title.Contains(searchTerms[1])
|| t.Description.Contains(searchTerms[1]))
)
.Select(t => t)).Cast<Item>().ToList();
case 3:
return (db.Tasks
.Where(t =>
(t.Title.Contains(searchTerms[0])
|| t.Description.Contains(searchTerms[0]))
&&
(t.Title.Contains(searchTerms[1])
|| t.Description.Contains(searchTerms[1]))
&&
(t.Title.Contains(searchTerms[2])
|| t.Description.Contains(searchTerms[2]))
)
.Select(t => t)).Cast<Item>().ToList();
default:
return null;
}
}
}

Ed, I've run into a similiar situation. The code is below. The important line of code is where I set the memberList variable. See if this fits your situation. Sorry if the formatting didn't come out to well.
Randy
// Get all the members that have an ActiveDirectorySecurityId matching one in the list.
IEnumerable<Member> members = database.Members
.Where(member => activeDirectoryIds.Contains(member.ActiveDirectorySecurityId))
.Select(member => member);
// This is necessary to avoid getting a "Queries with local collections are not supported"
//error in the next query.
memberList = members.ToList<Member>();
// Now get all the roles associated with the members retrieved in the first step.
IEnumerable<Role> roles = from i in database.MemberRoles
where memberList.Contains(i.Member)
select i.Role;

Since you cannot join local sequence with linq table, the only way to translate the above query into SQL woluld be to create WHERE clause with as many LIKE conditions as there are elements in searchTerms list (concatenated with AND operators). Apparently linq doesn't do that automatically and throws an expception instead.
But it can be done manually by iterating through the sequence:
public override List<Item> SearchListWithSearchPhrase(string searchPhrase)
{
List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase);
using (var db = Datasource.GetContext())
{
IQueryable<Task> taskQuery = db.Tasks.AsQueryable();
foreach(var term in searchTerms)
{
taskQuery = taskQuery.Where(t=>t.Title.ToUpper().Contains(term.ToUpper()) && t.Description.ToUpper().Contains(term.ToUpper()))
}
return taskQuery.ToList();
}
}
Mind that the query is still executed by DBMS as a SQL statement. The only drawback is that searchTerms list shouldn't be to long - otherwise the produced SQL statement won'tbe efficient.

Related

Factoring Linq Where clause for different EF objets [duplicate]

I'm getting the following error when trying to do a linq query:
LINQ to Entities does not recognize the method 'Boolean
IsCharityMatching(System.String, System.String)' method, and this
method cannot be translated into a store expression.
I've read lots of previous questions where people get the same error, and if I understand this correctly it's because LINQ to Entities requires the whole linq query expression to be translated to a server query, and therefore you can't call an outside method in it. I haven't been able to convert my scenario into something that works yet, and my brain is starting to melt down, so I was hoping someone could point me in the right direction. We're using Entity Framework and the specification pattern (and I'm new to both).
Here's the code that uses the specification:
ISpecification<Charity> specification = new CharitySearchSpecification(charityTitle, charityReference);
charities = charitiesRepository.Find(specification).OrderBy(p => p.RegisteredName).ToList();
Here's the linq expression:
public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
return p => p.IsCharityMatching(this.charityName, this.charityReference);
}
Here's the IsCharityMatching method:
public bool IsCharityMatching(string name, string referenceNumber)
{
bool exists = true;
if (!String.IsNullOrEmpty(name))
{
if (!this.registeredName.ToLower().Contains(name.ToLower()) &&
!this.alias.ToLower().Contains(name.ToLower()) &&
!this.charityId.ToLower().Contains(name.ToLower()))
{
exists = false;
}
}
if (!String.IsNullOrEmpty(referenceNumber))
{
if (!this.charityReference.ToLower().Contains(referenceNumber.ToLower()))
{
exists = false;
}
}
return exists;
}
Let me know if you need any more information.
Many thanks,
Annelie
As you've figured out, Entity Framework can't actually run your C# code as part of its query. It has to be able to convert the query to an actual SQL statement. In order for that to work, you will have to restructure your query expression into an expression that Entity Framework can handle.
public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
string name = this.charityName;
string referenceNumber = this.referenceNumber;
return p =>
(string.IsNullOrEmpty(name) ||
p.registeredName.ToLower().Contains(name.ToLower()) ||
p.alias.ToLower().Contains(name.ToLower()) ||
p.charityId.ToLower().Contains(name.ToLower())) &&
(string.IsNullOrEmpty(referenceNumber) ||
p.charityReference.ToLower().Contains(referenceNumber.ToLower()));
}
I got the same error in this code:
var articulos_en_almacen = xx.IV00102.Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();
this was the exactly error:
System.NotSupportedException: 'LINQ to Entities does not recognize the method 'Boolean Exists(System.Predicate`1[conector_gp.Models.almacenes_por_sucursal])' method, and this method cannot be translated into a store expression.'
I solved this way:
var articulos_en_almacen = xx.IV00102.ToList().Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();
I added a .ToList() before my table, this decouple the Entity and linq code, and avoid my next linq expression be translated
NOTE: this solution isn't optimal, because avoid entity filtering, and simply loads all table into memory
I ran into the same problem today, this was the first link I hit. However I was not looking for verifying my query. So if somebody else has the same issue and are looking for this solution it is added here. My issue was in another link.
It is the most common exception occurs when working with entity framework and converting data inside IQueryable result for filtering.
using (var context = new CustomerContext())
{
var item = context.InvoiceItems
.Where(i => i.Code == code.ToString())
.FirstOrDefault();
}
Several solutions exist. Move ToString() call to a separate line.
using (var context = new CustomerContext())
{
string codeStr = code.ToString();
var item = context.InvoiceItems
.Where(i => i.Code == codeStr)
.FirstOrDefault();
}
Use EF Extension Method,
using (var context = new CustomerContext())
{
var item = context.InvoiceItems
.Where(i => i.Code == SqlFunctions.StringConvert(code))
.FirstOrDefault();
}
Convert IQueryable result to IEnumerable before Filtering
using (var context = new CustomerContext())
{
var item = context.InvoiceItems.AsEnumerable()
.Where(i => i.Code == code.ToString())
.FirstOrDefault();
}
If anyone is looking for a VB.Net answer (as I was initially), here it is:
Public Function IsSatisfied() As Expression(Of Func(Of Charity, String, String, Boolean))
Return Function(charity, name, referenceNumber) (String.IsNullOrWhiteSpace(name) Or
charity.registeredName.ToLower().Contains(name.ToLower()) Or
charity.alias.ToLower().Contains(name.ToLower()) Or
charity.charityId.ToLower().Contains(name.ToLower())) And
(String.IsNullOrEmpty(referenceNumber) Or
charity.charityReference.ToLower().Contains(referenceNumber.ToLower()))
End Function
I got the same error in this code:
Solution
IQueryable to .toList() is the best option

LINQ to Entities ne reconnaît pas la méthode 'System.Decimal Parse(System.String)' [duplicate]

I'm getting the following error when trying to do a linq query:
LINQ to Entities does not recognize the method 'Boolean
IsCharityMatching(System.String, System.String)' method, and this
method cannot be translated into a store expression.
I've read lots of previous questions where people get the same error, and if I understand this correctly it's because LINQ to Entities requires the whole linq query expression to be translated to a server query, and therefore you can't call an outside method in it. I haven't been able to convert my scenario into something that works yet, and my brain is starting to melt down, so I was hoping someone could point me in the right direction. We're using Entity Framework and the specification pattern (and I'm new to both).
Here's the code that uses the specification:
ISpecification<Charity> specification = new CharitySearchSpecification(charityTitle, charityReference);
charities = charitiesRepository.Find(specification).OrderBy(p => p.RegisteredName).ToList();
Here's the linq expression:
public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
return p => p.IsCharityMatching(this.charityName, this.charityReference);
}
Here's the IsCharityMatching method:
public bool IsCharityMatching(string name, string referenceNumber)
{
bool exists = true;
if (!String.IsNullOrEmpty(name))
{
if (!this.registeredName.ToLower().Contains(name.ToLower()) &&
!this.alias.ToLower().Contains(name.ToLower()) &&
!this.charityId.ToLower().Contains(name.ToLower()))
{
exists = false;
}
}
if (!String.IsNullOrEmpty(referenceNumber))
{
if (!this.charityReference.ToLower().Contains(referenceNumber.ToLower()))
{
exists = false;
}
}
return exists;
}
Let me know if you need any more information.
Many thanks,
Annelie
As you've figured out, Entity Framework can't actually run your C# code as part of its query. It has to be able to convert the query to an actual SQL statement. In order for that to work, you will have to restructure your query expression into an expression that Entity Framework can handle.
public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
string name = this.charityName;
string referenceNumber = this.referenceNumber;
return p =>
(string.IsNullOrEmpty(name) ||
p.registeredName.ToLower().Contains(name.ToLower()) ||
p.alias.ToLower().Contains(name.ToLower()) ||
p.charityId.ToLower().Contains(name.ToLower())) &&
(string.IsNullOrEmpty(referenceNumber) ||
p.charityReference.ToLower().Contains(referenceNumber.ToLower()));
}
I got the same error in this code:
var articulos_en_almacen = xx.IV00102.Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();
this was the exactly error:
System.NotSupportedException: 'LINQ to Entities does not recognize the method 'Boolean Exists(System.Predicate`1[conector_gp.Models.almacenes_por_sucursal])' method, and this method cannot be translated into a store expression.'
I solved this way:
var articulos_en_almacen = xx.IV00102.ToList().Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();
I added a .ToList() before my table, this decouple the Entity and linq code, and avoid my next linq expression be translated
NOTE: this solution isn't optimal, because avoid entity filtering, and simply loads all table into memory
I ran into the same problem today, this was the first link I hit. However I was not looking for verifying my query. So if somebody else has the same issue and are looking for this solution it is added here. My issue was in another link.
It is the most common exception occurs when working with entity framework and converting data inside IQueryable result for filtering.
using (var context = new CustomerContext())
{
var item = context.InvoiceItems
.Where(i => i.Code == code.ToString())
.FirstOrDefault();
}
Several solutions exist. Move ToString() call to a separate line.
using (var context = new CustomerContext())
{
string codeStr = code.ToString();
var item = context.InvoiceItems
.Where(i => i.Code == codeStr)
.FirstOrDefault();
}
Use EF Extension Method,
using (var context = new CustomerContext())
{
var item = context.InvoiceItems
.Where(i => i.Code == SqlFunctions.StringConvert(code))
.FirstOrDefault();
}
Convert IQueryable result to IEnumerable before Filtering
using (var context = new CustomerContext())
{
var item = context.InvoiceItems.AsEnumerable()
.Where(i => i.Code == code.ToString())
.FirstOrDefault();
}
If anyone is looking for a VB.Net answer (as I was initially), here it is:
Public Function IsSatisfied() As Expression(Of Func(Of Charity, String, String, Boolean))
Return Function(charity, name, referenceNumber) (String.IsNullOrWhiteSpace(name) Or
charity.registeredName.ToLower().Contains(name.ToLower()) Or
charity.alias.ToLower().Contains(name.ToLower()) Or
charity.charityId.ToLower().Contains(name.ToLower())) And
(String.IsNullOrEmpty(referenceNumber) Or
charity.charityReference.ToLower().Contains(referenceNumber.ToLower()))
End Function
I got the same error in this code:
Solution
IQueryable to .toList() is the best option

LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable.Zip` (ASP.NET MVC) [duplicate]

I'm getting the following error when trying to do a linq query:
LINQ to Entities does not recognize the method 'Boolean
IsCharityMatching(System.String, System.String)' method, and this
method cannot be translated into a store expression.
I've read lots of previous questions where people get the same error, and if I understand this correctly it's because LINQ to Entities requires the whole linq query expression to be translated to a server query, and therefore you can't call an outside method in it. I haven't been able to convert my scenario into something that works yet, and my brain is starting to melt down, so I was hoping someone could point me in the right direction. We're using Entity Framework and the specification pattern (and I'm new to both).
Here's the code that uses the specification:
ISpecification<Charity> specification = new CharitySearchSpecification(charityTitle, charityReference);
charities = charitiesRepository.Find(specification).OrderBy(p => p.RegisteredName).ToList();
Here's the linq expression:
public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
return p => p.IsCharityMatching(this.charityName, this.charityReference);
}
Here's the IsCharityMatching method:
public bool IsCharityMatching(string name, string referenceNumber)
{
bool exists = true;
if (!String.IsNullOrEmpty(name))
{
if (!this.registeredName.ToLower().Contains(name.ToLower()) &&
!this.alias.ToLower().Contains(name.ToLower()) &&
!this.charityId.ToLower().Contains(name.ToLower()))
{
exists = false;
}
}
if (!String.IsNullOrEmpty(referenceNumber))
{
if (!this.charityReference.ToLower().Contains(referenceNumber.ToLower()))
{
exists = false;
}
}
return exists;
}
Let me know if you need any more information.
Many thanks,
Annelie
As you've figured out, Entity Framework can't actually run your C# code as part of its query. It has to be able to convert the query to an actual SQL statement. In order for that to work, you will have to restructure your query expression into an expression that Entity Framework can handle.
public System.Linq.Expressions.Expression<Func<Charity, bool>> IsSatisfied()
{
string name = this.charityName;
string referenceNumber = this.referenceNumber;
return p =>
(string.IsNullOrEmpty(name) ||
p.registeredName.ToLower().Contains(name.ToLower()) ||
p.alias.ToLower().Contains(name.ToLower()) ||
p.charityId.ToLower().Contains(name.ToLower())) &&
(string.IsNullOrEmpty(referenceNumber) ||
p.charityReference.ToLower().Contains(referenceNumber.ToLower()));
}
I got the same error in this code:
var articulos_en_almacen = xx.IV00102.Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();
this was the exactly error:
System.NotSupportedException: 'LINQ to Entities does not recognize the method 'Boolean Exists(System.Predicate`1[conector_gp.Models.almacenes_por_sucursal])' method, and this method cannot be translated into a store expression.'
I solved this way:
var articulos_en_almacen = xx.IV00102.ToList().Where(iv => alm_x_suc.Exists(axs => axs.almacen == iv.LOCNCODE.Trim())).Select(iv => iv.ITEMNMBR.Trim()).ToList();
I added a .ToList() before my table, this decouple the Entity and linq code, and avoid my next linq expression be translated
NOTE: this solution isn't optimal, because avoid entity filtering, and simply loads all table into memory
I ran into the same problem today, this was the first link I hit. However I was not looking for verifying my query. So if somebody else has the same issue and are looking for this solution it is added here. My issue was in another link.
It is the most common exception occurs when working with entity framework and converting data inside IQueryable result for filtering.
using (var context = new CustomerContext())
{
var item = context.InvoiceItems
.Where(i => i.Code == code.ToString())
.FirstOrDefault();
}
Several solutions exist. Move ToString() call to a separate line.
using (var context = new CustomerContext())
{
string codeStr = code.ToString();
var item = context.InvoiceItems
.Where(i => i.Code == codeStr)
.FirstOrDefault();
}
Use EF Extension Method,
using (var context = new CustomerContext())
{
var item = context.InvoiceItems
.Where(i => i.Code == SqlFunctions.StringConvert(code))
.FirstOrDefault();
}
Convert IQueryable result to IEnumerable before Filtering
using (var context = new CustomerContext())
{
var item = context.InvoiceItems.AsEnumerable()
.Where(i => i.Code == code.ToString())
.FirstOrDefault();
}
If anyone is looking for a VB.Net answer (as I was initially), here it is:
Public Function IsSatisfied() As Expression(Of Func(Of Charity, String, String, Boolean))
Return Function(charity, name, referenceNumber) (String.IsNullOrWhiteSpace(name) Or
charity.registeredName.ToLower().Contains(name.ToLower()) Or
charity.alias.ToLower().Contains(name.ToLower()) Or
charity.charityId.ToLower().Contains(name.ToLower())) And
(String.IsNullOrEmpty(referenceNumber) Or
charity.charityReference.ToLower().Contains(referenceNumber.ToLower()))
End Function
I got the same error in this code:
Solution
IQueryable to .toList() is the best option

Can't use .ToList() with IQueryable<T> [duplicate]

I'm migrating some stuff from one mysql server to a sql server but i can't figure out how to make this code work:
using (var context = new Context())
{
...
foreach (var item in collection)
{
IQueryable<entity> pages = from p in context.pages
where p.Serial == item.Key.ToString()
select p;
foreach (var page in pages)
{
DataManager.AddPageToDocument(page, item.Value);
}
}
Console.WriteLine("Done!");
Console.Read();
}
When it enters into the second foreach (var page in pages) it throws an exception saying:
LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression.
Anyone know why this happens?
Just save the string to a temp variable and then use that in your expression:
var strItem = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == strItem
select p;
The problem arises because ToString() isn't really executed, it is turned into a MethodGroup and then parsed and translated to SQL. Since there is no ToString() equivalent, the expression fails.
Note:
Make sure you also check out Alex's answer regarding the SqlFunctions helper class that was added later. In many cases it can eliminate the need for the temporary variable.
As others have answered, this breaks because .ToString fails to translate to relevant SQL on the way into the database.
However, Microsoft provides the SqlFunctions class that is a collection of methods that can be used in situations like this.
For this case, what you are looking for here is SqlFunctions.StringConvert:
from p in context.pages
where p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;
Good when the solution with temporary variables is not desirable for whatever reasons.
Similar to SqlFunctions you also have the EntityFunctions (with EF6 obsoleted by DbFunctions) that provides a different set of functions that also are data source agnostic (not limited to e.g. SQL).
The problem is that you are calling ToString in a LINQ to Entities query. That means the parser is trying to convert the ToString call into its equivalent SQL (which isn't possible...hence the exception).
All you have to do is move the ToString call to a separate line:
var keyString = item.Key.ToString();
var pages = from p in context.entities
where p.Serial == keyString
select p;
Cast table to Enumerable, then you call LINQ methods with using ToString() method inside:
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
But be careful, when you calling AsEnumerable or ToList methods because you will request all data from all entity before this method. In my case above I read all table_name rows by one request.
Had a similar problem.
Solved it by calling ToList() on the entity collection and querying the list.
If the collection is small this is an option.
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
Hope this helps.
Upgrading to Entity Framework Version 6.2.0 worked for me.
I was previously on Version 6.0.0.
Hope this helps,
Change it like this and it should work:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
The reason why the exception is not thrown in the line the LINQ query is declared but in the line of the foreach is the deferred execution feature, i.e. the LINQ query is not executed until you try to access the result. And this happens in the foreach and not earlier.
If you really want to type ToString inside your query, you could write an expression tree visitor that rewrites the call to ToString with a call to the appropriate StringConvert function:
using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;
namespace ToStringRewriting {
class ToStringRewriter : ExpressionVisitor {
static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
.Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
protected override Expression VisitMethodCall(MethodCallExpression node) {
var method = node.Method;
if (method.Name=="ToString") {
if (node.Object.GetType() == typeof(string)) { return node.Object; }
node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
}
return base.VisitMethodCall(node);
}
}
class Person {
string Name { get; set; }
long SocialSecurityNumber { get; set; }
}
class Program {
void Main() {
Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
var rewriter = new ToStringRewriter();
var finalExpression = rewriter.Visit(expr);
var dcx = new MyDataContext();
var query = dcx.Persons.Where(finalExpression);
}
}
}
In MVC, assume you are searching record(s) based on your requirement or information.
It is working properly.
[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{
EmployeeContext employeeContext = new EmployeeContext();
string searchby=formcollection["SearchBy"];
string value=formcollection["Value"];
if (formcollection["SearchBy"] == "Gender")
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
return View("Index", emplist);
}
else
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
return View("Index", emplist);
}
}
I got the same error in this case:
var result = Db.SystemLog
.Where(log =>
eventTypeValues.Contains(log.EventType)
&& (
search.Contains(log.Id.ToString())
|| log.Message.Contains(search)
|| log.PayLoad.Contains(search)
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
)
)
.OrderByDescending(log => log.Id)
.Select(r => r);
After spending way too much time debugging, I figured out that error appeared in the logic expression.
The first line search.Contains(log.Id.ToString()) does work fine, but the last line that deals with a DateTime object made it fail miserably:
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
Remove the problematic line and problem solved.
I do not fully understand why, but it seems as ToString() is a LINQ expression for strings, but not for Entities. LINQ for Entities deals with database queries like SQL, and SQL has no notion of ToString(). As such, we can not throw ToString() into a .Where() clause.
But how then does the first line work? Instead of ToString(), SQL have CAST and CONVERT, so my best guess so far is that linq for entities uses that in some simple cases. DateTime objects are not always found to be so simple...
My problem was that I had a 'text' data type for this column (due to a migration from sqlite).
Solution: just change the data type to 'nvarchar()' and regenerate the table.
Then Linq accepts the string comparison.
I am working on retiring Telerik Open Access and replacing it with Entity Framework 4.0. I came across same issue that telerik:GridBoundColumn filtering stopped working.
I find out that its not working only on System.String DataTypes. So I found this thread and solved it by just using .List() at the end of my Linq query as follows:
var x = (from y in db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();
Just turn the LINQ to Entity query into a LINQ to Objects query (e.g. call ToArray) anytime you need to use a method call in your LINQ query.

Use OrderBy in a LINQ predicate?

In my code I need to sort a collection either by Price or by Rating.TotalGrade and as you can see both LINQ querys are almost the same statement with only a minor difference.
I was thinking about using a LINQ predicate instead but as you can see the the orderby is the main difference and I found no sample using orderby in a query. Is it possible or are there other ways to shorten my code, Maybe there will be even more conditions in the future.
if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Position)
{
this.collectionCompleteSorted = new List<Result>(from co in collection
where co.IsVirtual == false
orderby co.Price, co.CurrentRanking
select co);
}
else if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Grade)
{
this.collectionCompleteSorted = new List<Result>(from co in collection
where co.IsVirtual == false
orderby co.Rating.TotalGrade, co.CurrentRanking
select co);
}
You can easily make use of deffered nature of LINQ and ability to easily compose queries.
Probably using code like this:
var baseQuery = from co in collection where !co.IsVirtual select co; // base of query
IOrderedEnumerable<Result> orderedQuery; // result of first ordering, must be of this type, so we are able to call ThenBy
switch(CurrentDisplayMode) // use enum here
{ // primary ordering based on enum
case CRSChartRankingGraphDisplayMode.Position: orderedQuery = baseQuery.OrderBy(co => co.Price);
break;
case CRSChartRankingGraphDisplayMode.Grade: orderedQuery = baseQuery.OrderBy(co => co.TotalGrade);
break;
}
this.collectionCompleteSorted = orderedQuery.ThenBy(co => co.CurrentRanking).ToList(); // secondary ordering and conversion to list
Its easy to understand and avoids converting to list until the very end.
If just your order by is different, than return your result into this.collectionCompleteSorted, and then do this.collectionCompleteSorted.OrderBy() when you enumerate through the data.
this.collectionCompleteSorted = new List<Result>(from co in collection
where co.IsVirtual == false
select co);
foreach (var obj in this.collectionCompleteSorted.OrderBy(c => c.Price).ToList())
{
// do something
}
And remove the order by remove your current linq query.
If the query is just being executed once, than you can leave off the .ToList() from the linq query in the example above. When ToList is called, this causes the query to be executed immediately, where if the OrderBy is implemented in a later call to the collection and a ToList is also, than the query would actually be executed on the database server with an order by statement, offloading the ordering from the code to the database. See http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx
Why not just grab the values (less the sort), then (as it appears) use a case to order the results?
// build your collection first
var items = from co in collection
where !co.IsVirtual
select co;
// go through your sort selectors
select (CurrentDisplayMode)
{
case CRSChartRankingGraphDisplayMode.Position:
this.collectionCompleteSorted = items.OrderBy(i => i.Price).ThenBy(j => j.CurrentRanking).ToList();
break;
case CRSChartRankingGraphDisplayMode.Grade:
this.collectionCompleteSorted = items.OrderBy(i => i.TotalGrade).ThenBy(j => j.CurrentRanking).ToList();
break;
...
//default: // maybe you want this, too
}
var q = collection.Where(co => !co.IsVirtual);
if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Position)
{
q = q.OrderBy(co => co.Price).ThenBy(co => co.CurrentRanking);
}
else if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Grade)
{
q = q.OrderBy(co => co.Rating.TotalGrade).ThenBy(co => co.CurrentRanking);
}
this.collectionCompleteSorted = q.ToList();

Categories

Resources