LINQ से एंटिटीज विधि 'System.String ToString ()' विधि को नहीं पहचानता है, और इस पद्धति को स्टोर अभिव्यक्ति में अनुवादित नहीं किया जा सकता है


126

मैं एक mysql सर्वर से एक sql सर्वर में कुछ सामान माइग्रेट कर रहा हूं, लेकिन मैं यह पता नहीं लगा सकता कि यह कोड कैसे काम करता है:

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

जब यह दूसरे में प्रवेश करता है तो foreach (var page in pages)यह एक अपवाद कहता है:

LINQ to Entities विधि 'System.String ToString ()' विधि को नहीं पहचानता है, और इस पद्धति का स्टोर अभिव्यक्ति में अनुवाद नहीं किया जा सकता है।

किसी को पता है कि ऐसा क्यों होता है?


जवाबों:


134

बस एक अस्थायी चर के लिए स्ट्रिंग को बचाने और फिर अपनी अभिव्यक्ति में उपयोग करें:

var strItem = item.Key.ToString();

IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == strItem
                           select p;

समस्या इसलिए उत्पन्न होती है क्योंकि ToString()वास्तव में निष्पादित नहीं किया जाता है, इसे मेथडग्रुप में बदल दिया जाता है और फिर पार्स किया जाता है और एसक्यूएल में अनुवाद किया जाता है। चूंकि कोई ToString()समकक्ष नहीं है, इसलिए अभिव्यक्ति विफल है।

ध्यान दें:

सुनिश्चित करें कि आप हेल्पर वर्ग के बारे में एलेक्स का जवाब भी देखें SqlFunctionsजो बाद में जोड़ा गया था। कई मामलों में यह अस्थायी चर की आवश्यकता को समाप्त कर सकता है।


14
क्या होगा अगर मेरे ToString () को समानता के बाईं ओर लागू किया जा रहा है? egpSerial.ToString () = आइटम।
डॉटनेट

3
@dotNet अभी भी विफल हो जाएगी क्योंकि पूरी चीज़ एक अभिव्यक्ति में बदल जाती है, जिसे एंटिटी फ्रेमवर्क वैध एसक्यूएल में बदलने की कोशिश करता है। कुछ तरीके हैं जो यह जानते हैं कि कैसे संभालना है, लेकिन ToString()उनमें से एक नहीं है।
जोश

7
@ जोश: मैं समझता हूँ कि यह विफल हो जाएगा। जो मैं पूछ रहा था वह उस परिदृश्य का समाधान है, क्योंकि उपरोक्त समाधान स्पष्ट रूप से वहां लागू नहीं किया जा सकता है।
डॉट

3
@ जोश: मैं ऐसे ही एक दर्शनीय स्थल के साथ संघर्ष कर रहा हूं। मान लें कि मेरा ऑर्डरनंबर कॉलम अंतर है, लेकिन मेरा उपयोगकर्ता ऑर्डर टाइप करने के लिए ऑर्डरनाइटर्स की सूची को फ़िल्टर करने में सक्षम होना चाहता है। यदि उसने खोज बॉक्स में 143 टाइप किया है, तो वह केवल उन रिकॉर्डों को चाहता है जिनके पास ऑर्डरनंबर लाइक% 143% है। । क्या मुझे इसे हासिल करने के लिए ऑर्डरनंबर कॉलम पर ToString () करने की आवश्यकता नहीं है?
डॉटनेट

5
@dotNET यह उन परिदृश्यों में से एक है जहाँ एक ORM नीचे की ओर गिरता है। मुझे लगता है कि उन स्थितियों में या तो सीधे एसक्यूएल के माध्यम से ExecuteQueryया एंटिटी एसक्यूएल का उपयोग करके ड्रॉप करना ठीक हैObjectQuery<T>
जोश

69

जैसा कि दूसरों ने उत्तर दिया है, यह टूट जाता है क्योंकि .Toring डेटाबेस में संबंधित एसक्यूएल के रास्ते पर अनुवाद करने में विफल रहता है।

हालाँकि, Microsoft SqlFunctions वर्ग प्रदान करता है जो इस तरह की स्थितियों में उपयोग की जा सकने वाली विधियों का एक संग्रह है।

इस मामले के लिए, आप यहाँ क्या देख रहे हैं SqlFunctions.StringConvert :

from p in context.pages
where  p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;

अच्छा है जब अस्थायी चर के साथ समाधान जो भी कारणों के लिए वांछनीय नहीं है।

SqlFunctions के समान ही आपके पास EntityFunctions ( EF6 के साथ DbFunctions द्वारा आक्षेपित ) भी हैं जो फ़ंक्शन का एक अलग सेट प्रदान करता है जो डेटा स्रोत अज्ञेय (जैसे एसक्यूएल तक सीमित नहीं) हैं।


4
उन्होंने .NET 4 में SqlFunctions वर्ग को वापस जोड़ा और मैं इसके बारे में सीख रहा हूं? बहुत बढ़िया मिल गया।
जेम्स स्कैप

24

समस्या यह है कि आप ToString को LINQ में Entities क्वेरी में बुला रहे हैं। इसका मतलब यह है कि पार्सर ToString कॉल को अपने समकक्ष SQL (जो संभव नहीं है ... इसलिए अपवाद) में बदलने की कोशिश कर रहा है।

आपको बस ToString कॉल को एक अलग लाइन में ले जाना है:

var keyString = item.Key.ToString();

var pages = from p in context.entities
            where p.Serial == keyString
            select p;

9

इसी तरह की समस्या थी। इकाई संग्रह पर टॉलिस्ट () को कॉल करके और सूची को क्वेरी करके इसे हल किया। यदि संग्रह छोटा है तो यह एक विकल्प है।

IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())

उम्मीद है की यह मदद करेगा।


42
कृपया ध्यान दें कि यह डेटाबेस से सभी पृष्ठ संस्थाओं को पुनः प्राप्त करेगा , और db के बजाय क्लाइंट की ओर से फ़िल्टरिंग करेगा .. आमतौर पर एक अच्छी बात नहीं है।
लैम्बिनेटर

3
यह सच है कि यह विधि किसी भी तालिका के लिए अक्षम होगी जिसमें एक से अधिक रिकॉर्ड होते हैं, जिसका अर्थ है सभी टेबल अस्तित्व में :-)। हालाँकि, इस जवाब ने आज मेरी मदद की क्योंकि मैं एक .Select प्रोजेक्शन कर रहा था जिसमें स्ट्रींग () शामिल थे। इसलिए कॉल करें। () हाथ से पहले मेरे लिए कोई परफॉर्मेंस पेनल्टी नहीं थी और कॉलिंग थी। टॉलिस्ट () ने मुझे .ToString () का उपयोग करने की अनुमति दी। प्रारूपण और मेरे। कथन का कथन ...
नाथन पूर्व

6

इसे इस तरह बदलें और इसे काम करना चाहिए:

var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == key
                           select p;

LINQ क्वेरी घोषित की गई लाइन में अपवाद को नहीं फेंके जाने का कारण है, लेकिन लाइन के foreachआस्थगित निष्पादन की सुविधा है, अर्थात LINQ क्वेरी को तब तक निष्पादित नहीं किया जाता है जब तक कि आप परिणाम तक पहुँचने का प्रयास नहीं करते हैं। और ऐसा foreachपहले और बाद में नहीं होता है ।


6

कास्ट टेबल के लिए Enumerable, तो आप LINQ तरीकों को ToString()अंदर विधि के साथ कहते हैं :

    var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)

लेकिन सावधान रहें, जब आप कॉल कर रहे हों AsEnumerableया ToListविधियाँ क्योंकि आप इस विधि से पहले सभी इकाई से सभी डेटा का अनुरोध करेंगे। ऊपर मेरे मामले में मैंने table_nameएक अनुरोध द्वारा सभी पंक्तियों को पढ़ा ।


5
आम तौर पर एक अच्छा विकल्प नहीं है, .AsEnumerable () मेमोरी में सभी डेटा डालते हैं, आप इसके बारे में और अधिक यहाँ देख सकते हैं: stackoverflow.com/questions/3311244/…
kavain

5

एंटिटी फ्रेमवर्क वर्जन में अपग्रेड करने के लिए 6.2.0 ने मेरे लिए काम किया।

मैं पहले संस्करण 6.0.0 पर था।

उम्मीद है की यह मदद करेगा,


1

MVC में, मान लें कि आप अपनी आवश्यकता या जानकारी के आधार पर रिकॉर्ड खोज रहे हैं। यह ठीक से काम कर रहा है।

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

2
एक बेहतर अभ्यास के लिए, या कोड के उत्पादन प्रकारों में, आपको हमेशा डेटाबेस की घटनाओं को एक सर्विस लेयर या डेटा लेयर में रखना चाहिए, न कि सीधे एक्शन में।
TGarrett

0

यदि आप वास्तव में ToStringअपनी क्वेरी के अंदर टाइप करना चाहते हैं , तो आप एक अभिव्यक्ति ट्री विज़िटर लिख सकते हैं जो उपयुक्त फ़ंक्शन को कॉल के ToStringसाथ कॉल को फिर से लिखता है :StringConvert

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

        }
    }
}

FirstOrDefault का उपयोग करना चाहिए और न केवल First ... यदि इसकी प्राथमिक कुंजी है, तो Find का उपयोग करें, क्योंकि यह बेहतर प्रदर्शन करता है।
TTarrett

@Tarrett यहां का एकमात्र उपयोग रिटर्न Firstके परिणामों पर GetMethods()है MethodInfo[]। AFAIK, MethodInfo[]एक Findविधि नहीं है , और न ही इस तरह के एक विस्तार विधि है। लेकिन मुझे वास्तव में उपयोग करना चाहिए Singleक्योंकि इस विधि को प्रतिबिंब के माध्यम से पाया जा रहा है, और यदि उपयुक्त विधि को हल नहीं किया जा सकता है तो संकलन-समय त्रुटि नहीं होगी।
Zev Spitz

0

मुझे इस मामले में वही त्रुटि मिली:

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

बहुत अधिक समय डिबगिंग का तरीका बिताने के बाद, मुझे पता लगा कि तर्क अभिव्यक्ति में त्रुटि दिखाई दी।

पहली पंक्ति search.Contains(log.Id.ToString())ठीक काम करती है, लेकिन अंतिम समय जो डेटटाइम ऑब्जेक्ट से संबंधित है, वह बुरी तरह विफल हो गया:

|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)

समस्याग्रस्त लाइन और हल की गई समस्या को दूर करें।

मुझे पूरी तरह से समझ में क्यों नहीं आता है, लेकिन ऐसा लगता है कि ट्रासिंग () स्ट्रिंग्स के लिए एक LINQ अभिव्यक्ति है, लेकिन एंटिटीज के लिए नहीं। LINQ एंटिटीज़ के लिए SQL, और SQL जैसे डेटाबेस प्रश्नों से संबंधित है, जिसमें ToString () की कोई धारणा नहीं है। जैसे, हम ToString () को .Where () क्लॉज में नहीं फेंक सकते।

लेकिन फिर पहली पंक्ति कैसे काम करती है? ToString () के बजाय, SQL CASTऔर है CONVERT, इसलिए मेरा अब तक का सबसे अच्छा अनुमान है कि संस्थाओं के लिए linq कुछ सरल मामलों में उपयोग करता है। डेटटाइम ऑब्जेक्ट हमेशा सरल नहीं पाए जाते हैं ...


-8

कभी भी LINQ को इकाई क्वेरी में LINQ से ऑब्जेक्ट क्वेरी (जैसे कॉल ToArray) में बदल दें, कभी भी आपको अपनी LINQ क्वेरी में विधि कॉल का उपयोग करने की आवश्यकता होती है।


3
"कभी भी आपको विधि कॉल का उपयोग करने की आवश्यकता होती है" खराब सलाह है - कई रिकॉर्ड के साथ यह एक बड़ी समस्या हो सकती है। इस परिदृश्य के लिए स्वीकृत उत्तर बहुत बेहतर है।
पेटीगो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.