একটি এক্সপ্রেশন ট্রি ল্যাম্বডায় কোনও নাল প্রচারকারী অপারেটর নাও থাকতে পারে


90

প্রশ্ন : price = co?.price ?? 0,নিম্নলিখিত কোডের লাইনটি আমাকে উপরের ত্রুটিটি দেয়। তবে আমি এটি ?থেকে co.?সরিয়ে ফেললে ঠিকঠাক কাজ করে। আমি এই এমএসডিএন উদাহরণটি অনুসরণ করার চেষ্টা করছিলাম যেখানে তারা ?লাইনে ব্যবহার করছে select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };তাই, কখন এবং কখন ব্যবহার ?করবেন ??না তা আমার বোঝা উচিত to

ত্রুটি :

একটি এক্সপ্রেশন ট্রি ল্যাম্বডায় কোনও নাল প্রচারকারী অপারেটর নাও থাকতে পারে

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,
                  ....
                  ....
              };
    ....
    ....
 }

দয়া করে ত্রুটিটি পোস্ট করুন ...
উইলেম ভ্যান অনসেম

4
ম্যান আমি আশা করি সি # এই সমর্থন!
নওফাল

উত্তর:


141

উদাহরণস্বরূপ আপনি ব্যবহারসমূহ থেকে উদ্ধৃত হয়েছে অবজেক্টস, যেখানে ক্যোয়ারীতে অন্তর্নিহিত ল্যামডা এক্সপ্রেশন রূপান্তরিত হয় LINQ প্রতিনিধিদের ... আপনি, মতিন বা অনুরূপ ব্যবহার করছেন যেহেতু সঙ্গে IQueryable<T>queryies, যেখানে ল্যামডা এক্সপ্রেশন রূপান্তরিত হয় অভিব্যক্তি গাছ । এক্সপ্রেশন গাছগুলি নাল কন্ডিশনাল অপারেটর (বা টিপলস) সমর্থন করে না।

এটি কেবল পুরানো উপায়ে করুন:

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

(আমি বিশ্বাস করি যে নাল-কোয়েলসিং অপারেটর একটি এক্সপ্রেশন ট্রিতে ভাল।


আপনি যদি ডায়নামিক লিনকিউ (সিস্টেম.লিনক.ডায়নামিক.কোর) ব্যবহার করছেন তবে আপনি np()পদ্ধতিটি ব্যবহার করতে পারেন । দেখুন github.com/StefH/System.Linq.Dynamic.Core/wiki/NullPropagation
Stef Heyenrath

10

আপনার ব্যবহারের সাথে লিঙ্ক করা কোড List<T>List<T>প্রয়োগ IEnumerable<T>কিন্তু না IQueryable<T>। সেক্ষেত্রে, প্রজেকশনটি মেমরিতে কার্যকর করা হয় এবং ?.কাজ করে।

আপনি কিছু ব্যবহার করছেন IQueryable<T>, যা খুব আলাদাভাবে কাজ করে। কারণ IQueryable<T>, অভিক্ষেপের একটি প্রতিনিধিত্ব তৈরি করা হয় এবং আপনার লিনকিউ সরবরাহকারী রানটাইমের সময় এটি দিয়ে কী করবেন তা স্থির করে। পিছনের সামঞ্জস্যতার কারণে, ?.এখানে ব্যবহার করা যাবে না।

আপনার লিনকিউ সরবরাহকারীর উপর নির্ভর করে আপনি প্লেইনটি ব্যবহার করতে সক্ষম হতে পারেন .এবং এখনও কোনও পান না NullReferenceException


@hvd আপনি কি ব্যাখ্যা করতে পারবেন যে পিছনের সামঞ্জস্যের জন্য এটি কেন প্রয়োজন?
জাগ

4
@ জাগ সমস্ত লিনিকিউ সরবরাহকারী যা ইতিমধ্যে প্রবর্তনের আগে ?.তৈরি করা ?.হয়েছিল কোনও যুক্তিসঙ্গত উপায়ে পরিচালনা করতে প্রস্তুত হত না ।

4
তবে ?.নতুন অপারেটর নং কী? সুতরাং পুরানো কোড ব্যবহার করবে না ?.এবং এইভাবে ভঙ্গ হবে না। লিনক সরবরাহকারী সিএলআর পদ্ধতিগুলির মতো আরও অনেক কিছুই পরিচালনা করতে প্রস্তুত নয়।
জাগ

4
@ জাগ রাইট, পুরানো লিনকিউ সরবরাহকারীদের সাথে একত্রে পুরানো কোডটি প্রভাবিত হবে না। পুরানো কোড ব্যবহার করা হবে না ?.। নতুন কোড পুরাতন LINQ প্রদানকারী, যা ব্যবহার করে হতে পারে হয় , (একটি ব্যতিক্রম নিক্ষেপ করে) CLR পদ্ধতি তারা চিনতে না হ্যান্ডেল করতে প্রস্তুত ঐ হইয়া চমত্কারভাবে বিদ্যমান অভিব্যক্তি গাছ অবজেক্ট মডেল মধ্যে যেহেতু। সম্পূর্ণ নতুন অভিব্যক্তি গাছ নোড ধরনের খাপ খায় না।

4
ইতিমধ্যে লিনকিউ সরবরাহকারীদের ফেলে দেওয়া ব্যতিক্রম সংখ্যাটি দেওয়া, খুব কমই একটি উপযুক্ত বাণিজ্য বলে মনে হচ্ছে - "আমরা সমর্থন করি না, তাই আমরা কখনই করতে পারি না"
নেটমেজ

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

যদিও অভিব্যক্তি ট্রি সি # 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");
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.