एक अभिव्यक्ति ट्री लैम्ब्डा में अशक्त प्रचारक संचालक नहीं हो सकता है


96

प्रश्न : price = co?.price ?? 0,निम्नलिखित कोड में लाइन मुझे उपरोक्त त्रुटि देती है। लेकिन अगर मैं हटाने ?से co.?यह ठीक काम करता है। मैं इस MSDN उदाहरण का अनुसरण करने की कोशिश कर रहा था जहां वे ?लाइन पर उपयोग कर रहे हैं select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };, इसलिए ऐसा लगता है कि मुझे यह समझने की आवश्यकता है कि कब और किसके ?साथ उपयोग ??करना है।

त्रुटि :

एक अभिव्यक्ति ट्री लैम्ब्डा में अशक्त प्रचारक संचालक नहीं हो सकता है

public class CustomerOrdersModelView
{
    public string CustomerID { get; set; }
    public int FY { get; set; }
    public float? price { get; set; }
    ....
    ....
}
public async Task<IActionResult> ProductAnnualReport(string rpt)
{
    var qry = from c in _context.Customers
              join ord in _context.Orders
                on c.CustomerID equals ord.CustomerID into co
              from m in co.DefaultIfEmpty()
              select new CustomerOrdersModelView
              {
                  CustomerID = c.CustomerID,
                  FY = c.FY,
                  price = co?.price ?? 0,
                  ....
                  ....
              };
    ....
    ....
 }

कृपया त्रुटि पोस्ट करें ...
विलेम वैन ओन्सेम

3
यार मैं काश C # इस का समर्थन करता!
नवफाल

जवाबों:


150

उदाहरण के लिए आप LINQ का उपयोग ऑब्जेक्ट्स से कर रहे थे, जहाँ क्वेरी में निहित लैम्ब्डा एक्सप्रेशंस को डेलिगेट्स में बदल दिया जाता है ... जबकि आप EF या समान का उपयोग क्वेरी के साथ IQueryable<T>करते हैं, जहाँ लैम्ब्डा एक्सप्रेशन को एक्सप्रेशन ट्री में परिवर्तित किया जाता है । अभिव्यक्ति के पेड़ अशक्त सशर्त ऑपरेटर (या टुपल्स) का समर्थन नहीं करते हैं।

बस इसे पुराने तरीके से करें:

price = co == null ? 0 : (co.price ?? 0)

(मेरा मानना ​​है कि अभिव्यक्ति के पेड़ में नल-कोलेसिंग ऑपरेटर ठीक है।)


यदि आप डायनामिक LINQ (System.Linq.Dynamic.Core) का उपयोग कर रहे हैं, तो आप np()विधि का उपयोग कर सकते हैं । देखें github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagation
Stef Heyenrath

11

आपके द्वारा उपयोग किए जाने वाला कोड List<T>List<T>लागू होता है, IEnumerable<T>लेकिन नहीं IQueryable<T>। उस स्थिति में, प्रक्षेपण को स्मृति और ?.कार्यों में निष्पादित किया जाता है ।

आप कुछ का उपयोग कर रहे हैं IQueryable<T>, जो बहुत अलग तरीके से काम करता है। के लिए IQueryable<T>, प्रक्षेपण का एक प्रतिनिधित्व बनाया जाता है, और आपका LINQ प्रदाता यह तय करता है कि रनटाइम में इसके साथ क्या करना है। पश्चगामी संगतता कारणों के लिए, ?.यहां उपयोग नहीं किया जा सकता है।

आपके LINQ प्रदाता के आधार पर, आप सादे का उपयोग करने में सक्षम हो सकते हैं .और फिर भी कोई नहीं मिल सकता है NullReferenceException


@hvd आपको बता सकता है कि बैकवर्ड संगतता के लिए इसकी आवश्यकता क्यों है?
जग

1
@ जैग सभी LINQ प्रोवाइडर्स जो कि शुरू होने से पहले ही बन चुके थे किसी भी वाजिब तरीके ?.से संभालने के लिए तैयार नहीं थे ?.

1
लेकिन ?.क्या कोई नया ऑपरेटर नहीं है? इसलिए पुराने कोड का उपयोग ?.नहीं किया जाएगा और इस प्रकार तोड़ा नहीं जाएगा। लिनक प्रोवाइडर कई अन्य चीजों जैसे सीएलआर तरीकों को संभालने के लिए तैयार नहीं हैं।
गुड़

2
@ जैग राइट, पुराने LINQ प्रदाताओं के साथ पुराने कोड प्रभावित नहीं होंगे। पुराने कोड का उपयोग नहीं किया जाएगा ?.। नए कोड पुराने LINQ प्रदाताओं का उपयोग कर सकते हैं, जो उन CLR विधियों को संभालने के लिए तैयार किए जाते हैं , जिन्हें वे पहचान नहीं पाते हैं (अपवाद को फेंककर), क्योंकि वे मौजूदा अभिव्यक्ति ट्री ऑब्जेक्ट मॉडल में अच्छी तरह से फिट होते हैं। पूरी तरह से नई अभिव्यक्ति ट्री नोड प्रकार में फिट नहीं है।

3
LINQ प्रदाताओं द्वारा पहले ही फेंक दिए गए अपवादों की संख्या को देखते हुए, शायद ही कोई सार्थक व्यापार बंद लगता है - "हम समर्थन नहीं करते थे, इसलिए हम कभी नहीं कर सकते थे"
NetMage

1

जॉन स्कीट का जवाब सही था, मेरे मामले में मैं DateTimeअपने एंटिटी वर्ग के लिए उपयोग कर रहा था । जब मैंने जैसे उपयोग करने की कोशिश की

(a.DateProperty == null ? default : a.DateProperty.Date)

मेरे पास त्रुटि थी

Property 'System.DateTime Date' is not defined for type 'System.Nullable`1[System.DateTime]' (Parameter 'property')

इसलिए मुझे DateTime?अपनी इकाई वर्ग और के लिए बदलने की जरूरत है

(a.DateProperty == null ? default : a.DateProperty.Value.Date)

यह अशक्त प्रसार संचालक के बारे में नहीं है।
गर्ट अर्नोल्ड

मुझे पसंद है कि आप कैसे उल्लेख करते हैं कि जॉन स्कीट सही था, यह सुझाव देता है कि यह किसी भी तरह से उसके लिए गलत हो सकता है। अच्छा था!
क्लिकर

0

जबकि अभिव्यक्ति ट्री C # 6.0 नल के प्रचार का समर्थन नहीं करता है, हम क्या कर सकते हैं एक आगंतुक बना सकते हैं जो ऑपरेटर की तरह ही सुरक्षित नल प्रसार के लिए अभिव्यक्ति पेड़ को संशोधित करता है!

यह रहा मेरा:

public class NullPropagationVisitor : ExpressionVisitor
{
    private readonly bool _recursive;

    public NullPropagationVisitor(bool recursive)
    {
        _recursive = recursive;
    }

    protected override Expression VisitUnary(UnaryExpression propertyAccess)
    {
        if (propertyAccess.Operand is MemberExpression mem)
            return VisitMember(mem);

        if (propertyAccess.Operand is MethodCallExpression met)
            return VisitMethodCall(met);

        if (propertyAccess.Operand is ConditionalExpression cond)
            return Expression.Condition(
                    test: cond.Test,
                    ifTrue: MakeNullable(Visit(cond.IfTrue)),
                    ifFalse: MakeNullable(Visit(cond.IfFalse)));

        return base.VisitUnary(propertyAccess);
    }

    protected override Expression VisitMember(MemberExpression propertyAccess)
    {
        return Common(propertyAccess.Expression, propertyAccess);
    }

    protected override Expression VisitMethodCall(MethodCallExpression propertyAccess)
    {
        if (propertyAccess.Object == null)
            return base.VisitMethodCall(propertyAccess);

        return Common(propertyAccess.Object, propertyAccess);
    }

    private BlockExpression Common(Expression instance, Expression propertyAccess)
    {
        var safe = _recursive ? base.Visit(instance) : instance;
        var caller = Expression.Variable(safe.Type, "caller");
        var assign = Expression.Assign(caller, safe);
        var acess = MakeNullable(new ExpressionReplacer(instance,
            IsNullableStruct(instance) ? caller : RemoveNullable(caller)).Visit(propertyAccess));
        var ternary = Expression.Condition(
                    test: Expression.Equal(caller, Expression.Constant(null)),
                    ifTrue: Expression.Constant(null, acess.Type),
                    ifFalse: acess);

        return Expression.Block(
            type: acess.Type,
            variables: new[]
            {
                caller,
            },
            expressions: new Expression[]
            {
                assign,
                ternary,
            });
    }

    private static Expression MakeNullable(Expression ex)
    {
        if (IsNullable(ex))
            return ex;

        return Expression.Convert(ex, typeof(Nullable<>).MakeGenericType(ex.Type));
    }

    private static bool IsNullable(Expression ex)
    {
        return !ex.Type.IsValueType || (Nullable.GetUnderlyingType(ex.Type) != null);
    }

    private static bool IsNullableStruct(Expression ex)
    {
        return ex.Type.IsValueType && (Nullable.GetUnderlyingType(ex.Type) != null);
    }

    private static Expression RemoveNullable(Expression ex)
    {
        if (IsNullableStruct(ex))
            return Expression.Convert(ex, ex.Type.GenericTypeArguments[0]);

        return ex;
    }

    private class ExpressionReplacer : ExpressionVisitor
    {
        private readonly Expression _oldEx;
        private readonly Expression _newEx;

        internal ExpressionReplacer(Expression oldEx, Expression newEx)
        {
            _oldEx = oldEx;
            _newEx = newEx;
        }

        public override Expression Visit(Expression node)
        {
            if (node == _oldEx)
                return _newEx;

            return base.Visit(node);
        }
    }
}

यह निम्नलिखित परीक्षणों से गुजरता है:

private static string Foo(string s) => s;

static void Main(string[] _)
{
    var visitor = new NullPropagationVisitor(recursive: true);

    Test1();
    Test2();
    Test3();

    void Test1()
    {
        Expression<Func<string, char?>> f = s => s == "foo" ? 'X' : Foo(s).Length.ToString()[0];

        var fBody = (Expression<Func<string, char?>>)visitor.Visit(f);

        var fFunc = fBody.Compile();

        Debug.Assert(fFunc(null) == null);
        Debug.Assert(fFunc("bar") == '3');
        Debug.Assert(fFunc("foo") == 'X');
    }

    void Test2()
    {
        Expression<Func<string, int>> y = s => s.Length;

        var yBody = visitor.Visit(y.Body);
        var yFunc = Expression.Lambda<Func<string, int?>>(
                                    body: yBody,
                                    parameters: y.Parameters)
                            .Compile();

        Debug.Assert(yFunc(null) == null);
        Debug.Assert(yFunc("bar") == 3);
    }

    void Test3()
    {
        Expression<Func<char?, string>> y = s => s.Value.ToString()[0].ToString();

        var yBody = visitor.Visit(y.Body);
        var yFunc = Expression.Lambda<Func<char?, string>>(
                                    body: yBody,
                                    parameters: y.Parameters)
                            .Compile();

        Debug.Assert(yFunc(null) == null);
        Debug.Assert(yFunc('A') == "A");
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.