137

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?

Erre Efe
  • 15,387
  • 10
  • 45
  • 77
  • Related post - [LINQ to Entities does not recognize the method](https://stackoverflow.com/q/7259567/465053) & [Entity Framework Specification Pattern Implementation](https://stackoverflow.com/q/2352764/465053) – RBT Mar 01 '19 at 11:46
  • This is now an outdated issue. Later EF versions do translate ToString into SQL for most data types. But in this case the fix is much easier, see the accepted answer, that all later answerers should have looked at first. – Gert Arnold May 23 '22 at 19:29

13 Answers13

140

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.

Community
  • 1
  • 1
Josh
  • 44,706
  • 7
  • 102
  • 124
  • 17
    What if my ToString() is being applied on the left-hand-side of equality? e.g. p.Serial.ToString() = item. – dotNET Mar 26 '13 at 18:46
  • 3
    @dotNet That will still fail because the whole thing get's turned into an Expression, which Entity Framework tries to turn into valid SQL. There are some methods it knows how to handle, but `ToString()` isn't one of them. – Josh Mar 27 '13 at 01:26
  • 7
    @Josh: I understand that it will fail. What I was asking for is a solution of that scenario, because the above solution obviously cannot be applied there. – dotNET Mar 27 '13 at 04:18
  • 1
    @dotNET Well... in this particular scenario it already is a string, so it's a moot point. Also, there isn't any scenario I can think of where you would need to do this. Since you are trying to match on a column, then you know the type you are trying to match against. – Josh Mar 27 '13 at 05:38
  • 4
    @Josh: I'm struggling with one such scenrio. Say my OrderNumber column is int, but my user wants to be able to filter the list of OrderNumbers as he types in. If he has typed 143 in the search box, he wants only those records that have an OrderNumber LIKE '%143%'. Don't I need to do ToString() on OrderNumber column to achieve it? – dotNET Mar 27 '13 at 06:01
  • 5
    @dotNET this is one of those scenarios where an ORM falls down on it's face. I think it's ok in those situations to drop down into either straight SQL via `ExecuteQuery` or by using Entity SQL with `ObjectQuery` – Josh Mar 27 '13 at 12:15
  • @Josh: +1 for referring to Entity SQL with ObjectQuery. I didn't know this monster existed. Seems to be opening a whole world of possibilities for me. – dotNET Mar 28 '13 at 05:53
  • It is actually not a scenario where it falls on it's face. An order number is possibly as numeric indicator, it is not an int. As such, storing it as an int is - already a database mistake. I do not expect EF to safe my from my own bad programming. – TomTom Jan 10 '16 at 18:17
  • @dotNET There is a [`StringConvert` method](http://stackoverflow.com/a/24124190), which when included in the expression tree, Entity Framework / LINQ-to-SQL will rewrite as a call to the T-SQL `CONVERT` function. – Zev Spitz Sep 29 '16 at 23:54
  • Just a nit, I don't think "method groups" have anything to do with this. `ToString()` specifies unambiguously which one of the overloads, if any, is to be used (the one with no arguments). What the call gets converted to is a [MethodCallExpression](https://msdn.microsoft.com/en-us/library/system.linq.expressions.methodcallexpression(v=vs.110).aspx), or an internal derivative of it. A MethodCallExpression references a specific method, not a method group. – Matti Virkkunen Apr 03 '17 at 16:14
  • What if what we need to convert is p.Serial, which is part of the entity? I'm currently needing something like that but for formating a DateTime – Raul Marquez Sep 12 '19 at 00:04
  • My team members have the same code and they are not getting this exception,So I thought this is a version issue.I have update my Visual studio but only I am the one who faced this exception. Why is that so? – Sibtain Feb 14 '20 at 06:55
77

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).

Alex
  • 14,104
  • 11
  • 54
  • 77
  • 4
    They added the SqlFunctions class back in .NET 4 and I'm just learning about it? Excellent find. – James Skemp Mar 25 '15 at 17:03
  • It is very important that you add .Trim() at the end so that the query succeeds. Eg: SqlFunctions.StringConvert((double)item.Key.Id).Trim(). Looks like the StringConvert function pads the string with blank spaces. – Gautam Jain Aug 02 '23 at 08:16
25

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;
Erre Efe
  • 15,387
  • 10
  • 45
  • 77
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
15

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.

kkost
  • 3,640
  • 5
  • 41
  • 72
  • 8
    normally is not a good choice, .AsEnumerable() put all data in memory, you can see more about it here: http://stackoverflow.com/questions/3311244/understanding-asenumerable-in-linq-to-sql – kavain Apr 11 '17 at 19:30
12

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.

Keith Sirmons
  • 8,271
  • 15
  • 52
  • 75
cynicaldoctor
  • 353
  • 2
  • 8
  • 50
    Please note this will retrieve *all* Page entities from the database, and do the filtering on the client side instead of the db.. usually not a good thing. – lambinator Mar 12 '13 at 00:06
  • 3
    It's true this method would be inefficient for any table that contains more than one record, meaning all tables in existence :-). However, this answer did help me today because I was doing a .Select projection that included toString() so calling .ToList() before hand had no performance penalty for me and calling .ToList() allowed me to use the .ToString() formatting and my .Select statement... – Nathan Prather Aug 29 '13 at 15:48
10

Upgrading to Entity Framework Version 6.2.0 worked for me.

I was previously on Version 6.0.0.

Hope this helps,

93Ramadan
  • 343
  • 3
  • 8
  • query.where(x => someGuidAsString.Contains(x.AGuid.ToString())) -> does not works on EF 6.0.0.0; however, works onward 6.1.3 (has tested on 6.1.3 & 6.4.4) – curious.netter Apr 22 '21 at 12:17
6

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.

Erre Efe
  • 15,387
  • 10
  • 45
  • 77
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
1

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);
    }         
}
Gilad Green
  • 36,708
  • 7
  • 61
  • 95
shakti
  • 11
  • 1
  • 2
    For a better practice, or in production types of code, you should always have the database events in a service layer or data layer and not directly in the action. – TGarrett Oct 04 '16 at 15:26
1

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);

        }
    }
}
Community
  • 1
  • 1
Zev Spitz
  • 13,950
  • 6
  • 64
  • 136
  • Should be using FirstOrDefault and not just First... If its a primary key, then use Find, as that performs better. – TGarrett Oct 04 '16 at 15:29
  • @TGarrett The only usage of `First` here is on the results of `GetMethods()` which returns `MethodInfo[]`. AFAIK, `MethodInfo[]` doesn't have a `Find` method, nor is there such an extension method. But i really should use `Single` because this method is being found via reflection, and there won't be a compile-time error if the appropriate method can't be resolved. – Zev Spitz Oct 04 '16 at 18:44
0

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...

pekaaw
  • 2,309
  • 19
  • 18
0

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.

Jaume
  • 3,672
  • 19
  • 60
  • 119
  • How is this related to the `ToString` issue? – Gert Arnold Jan 15 '22 at 10:14
  • ToString was not accepted if the database field has a 'text' format. If you change the type to varchar or nvarchar, ToString will be accepted and conversion will be succeed. For me, it was the solution – Jaume Jan 19 '22 at 12:05
-2

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();
        
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 1
    It's really not useful to bring in another case in a completely different context. Also, it's unclear what actually was wrong ("stopped working" can be anything) and how the LINQ query fixed it. – Gert Arnold May 24 '22 at 06:59
-8

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.

T. Webster
  • 9,605
  • 6
  • 67
  • 94
  • 3
    "anytime you need to use a method call" is poor advice - with many records this could be a big problem. The accepted answer is much better for this scenario. – PeteGO May 01 '13 at 19:16