LINQ से एंटिटीज केवल EDM आदिम इंटरफेस के साथ EDM आदिम या एन्यूमरेशन कास्टिंग का समर्थन करता है


96

मेरे पास निम्नलिखित सामान्य विस्तार विधि है:

public static T GetById<T>(this IQueryable<T> collection, Guid id) 
    where T : IEntity
{
    Expression<Func<T, bool>> predicate = e => e.Id == id;

    T entity;

    // Allow reporting more descriptive error messages.
    try
    {
        entity = collection.SingleOrDefault(predicate);
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException(string.Format(
            "There was an error retrieving an {0} with id {1}. {2}",
            typeof(T).Name, id, ex.Message), ex);
    }

    if (entity == null)
    {
        throw new KeyNotFoundException(string.Format(
            "{0} with id {1} was not found.",
            typeof(T).Name, id));
    }

    return entity;
}

दुर्भाग्यवश Entity Framework को यह नहीं पता है कि predicateC को कैसे हैंडल किया जाए क्योंकि C # को विधेय में परिवर्तित किया गया है:

e => ((IEntity)e).Id == id

एंटिटी फ्रेमवर्क निम्नलिखित अपवाद फेंकता है:

'SomeEntity' टाइप करने के लिए 'IEntity' टाइप करने में असमर्थ। LINQ to Entities केवल EDM आदिम या गणना प्रकार का समर्थन करता है।

हम अपने IEntityइंटरफेस के साथ एंटिटी फ्रेमवर्क कैसे काम कर सकते हैं ?

जवाबों:


188

मैं classविस्तार विधि में सामान्य प्रकार की बाधा को जोड़कर इसे हल करने में सक्षम था । मुझे यकीन नहीं है कि यह क्यों काम करता है, हालांकि।

public static T GetById<T>(this IQueryable<T> collection, Guid id)
    where T : class, IEntity
{
    //...
}

6
मेरे लिए भी काम करता है! मैं किसी को यह समझाने में सक्षम होना पसंद करूंगा। #linqblackmagic
berko

क्या आप यह समझा सकते हैं कि आपने इस बाधा को कैसे जोड़ा
यरहमान

5
मेरा अनुमान है कि इंटरफ़ेस प्रकार के बजाय वर्ग प्रकार का उपयोग किया जाता है। EF इंटरफ़ेस प्रकार के बारे में नहीं जानता है इसलिए यह इसे SQL में परिवर्तित नहीं कर सकता है। वर्ग बाधा के साथ अनुमान लगाया जाने वाला प्रकार DbSet <T> प्रकार है जो EF जानता है कि उसके साथ क्या करना है।
20

1
बिल्कुल सही, यह इंटरफ़ेस-आधारित क्वेरी करने में सक्षम है और अभी भी संग्रह को IQueryable के रूप में बनाए रखता है। हालांकि, ईएफ के आंतरिक कामकाज को जाने बिना, इस बात को सोचने का एक तरीका है कि मूल रूप से यह तय करने का कोई तरीका नहीं है।
एंडर्स

यहाँ जो आप देख रहे हैं वह एक संकलक समय बाधा है जो C # संकलक को यह निर्धारित करने की अनुमति देता है कि T पद्धति के भीतर टाइप IEntity है इसलिए यह निर्धारित करने में सक्षम है कि IEntity "सामान" का कोई भी उपयोग संकलित समय के दौरान मान्य है जो MSST कोड उत्पन्न करता है क्या कॉल करने से पहले ऑटो आपके लिए यह जाँच करेगा। स्पष्ट करने के लिए, एक प्रकार की बाधा के रूप में "वर्ग" को जोड़ने से संग्रह की सुविधा मिलती है। FirstOrDefault () को सही ढंग से चलाने की अनुमति देता है क्योंकि यह संभवतया एक वर्ग आधारित प्रकार पर एक डिफ़ॉल्ट ctor को बुलाकर T का एक नया उदाहरण देता है।
युद्ध

64

class"फिक्स" के बारे में कुछ अतिरिक्त स्पष्टीकरण ।

यह उत्तर दो अलग-अलग अभिव्यक्तियों को दिखाता है, एक साथ और दूसरा बिना किसी where T: classबाधा के। classहमारे पास बाधा के बिना :

e => e.Id == id // becomes: Convert(e).Id == id

और बाधा के साथ:

e => e.Id == id // becomes: e.Id == id

इन दो अभिव्यक्तियों को इकाई रूपरेखा द्वारा अलग तरीके से व्यवहार किया जाता है। ईएफ 6 स्रोतों को देखते हुए , कोई पा सकता है कि अपवाद यहांValidateAndAdjustCastTypes() से आता है, देखें

ऐसा क्या होता है, कि ईएफ IEntityकुछ ऐसा करने की कोशिश करता है जो डोमेन मॉडल की दुनिया को समझ में आता है, हालांकि ऐसा करने में वह विफल रहता है, इसलिए अपवाद को फेंक दिया जाता है।

classबाधा के साथ अभिव्यक्ति में Convert()ऑपरेटर शामिल नहीं है , कलाकारों की कोशिश नहीं की जाती है और सब कुछ ठीक है।

यह अभी भी खुला प्रश्न बना हुआ है, LINQ विभिन्न अभिव्यक्तियों का निर्माण क्यों करता है? मुझे उम्मीद है कि कुछ C # विज़ार्ड इसे समझाने में सक्षम होंगे।


1
स्पष्टीकरण के लिए धन्यवाद।
जैस रिया

9
@JonSkeet किसी ने यहाँ एक C # जादूगर को बुलाने की कोशिश की। आप कहाँ हैं?
निक एन।

23

इकाई फ्रेमवर्क इस बॉक्स से बाहर का समर्थन नहीं करता है, लेकिन यह एक ExpressionVisitorअभिव्यक्ति का अनुवाद आसानी से लिखा है:

private sealed class EntityCastRemoverVisitor : ExpressionVisitor
{
    public static Expression<Func<T, bool>> Convert<T>(
        Expression<Func<T, bool>> predicate)
    {
        var visitor = new EntityCastRemoverVisitor();

        var visitedExpression = visitor.Visit(predicate);

        return (Expression<Func<T, bool>>)visitedExpression;
    }

    protected override Expression VisitUnary(UnaryExpression node)
    {
        if (node.NodeType == ExpressionType.Convert && node.Type == typeof(IEntity))
        {
            return node.Operand;
        }

        return base.VisitUnary(node);
    }
}

केवल एक चीज जो आपको करनी है वह है कि अभिव्यक्ति आगंतुक का उपयोग करके इसे विधेय में रूपांतरित करें:

public static T GetById<T>(this IQueryable<T> collection, 
    Expression<Func<T, bool>> predicate, Guid id)
    where T : IEntity
{
    T entity;

    // Add this line!
    predicate = EntityCastRemoverVisitor.Convert(predicate);

    try
    {
        entity = collection.SingleOrDefault(predicate);
    }

    ...
}

एक और-रहित लचीला- दृष्टिकोण का उपयोग करना है DbSet<T>.Find:

// NOTE: This is an extension method on DbSet<T> instead of IQueryable<T>
public static T GetById<T>(this DbSet<T> collection, Guid id) 
    where T : class, IEntity
{
    T entity;

    // Allow reporting more descriptive error messages.
    try
    {
        entity = collection.Find(id);
    }

    ...
}

1

मैं एक ही त्रुटि लेकिन एक समान लेकिन अलग समस्या थी। मैं एक एक्सटेंशन फ़ंक्शन बनाने की कोशिश कर रहा था जो IQueryable को लौटाता है लेकिन फिल्टर मानदंड बेस क्लास पर आधारित था।

मुझे अंततः वह समाधान मिला जो मेरी एक्सटेंशन पद्धति को कॉल करने के लिए था। चयन (ई => ई के रूप में टी) जहां टी बाल वर्ग है और ई आधार वर्ग है।

पूर्ण विवरण यहाँ हैं: EF में बेस क्लास का उपयोग करके IQueryable <T> एक्सटेंशन बनाएं

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.