দুটি এক্সপ্রেশন সম্মিলন (এক্সপ্রেশন <ফানক <টি, বুল >>)


248

আমার টাইপের দুটি এক্সপ্রেশন রয়েছে Expression<Func<T, bool>>এবং আমি ওআর, এবং না এর মধ্যে নিতে চাই এবং একই ধরণের একটি নতুন এক্সপ্রেশন পেতে চাই

Expression<Func<T, bool>> expr1;
Expression<Func<T, bool>> expr2;

...

//how to do this (the code below will obviously not work)
Expression<Func<T, bool>> andExpression = expr AND expr2

উত্তর:


330

ওয়েল, আপনি লজিক্যাল এক্সপ্রেশন একত্রিত করতে Expression.AndAlso/ OrElseইত্যাদি ব্যবহার করতে পারেন , তবে সমস্যাটি পরামিতিগুলি; আপনি কি একই ParameterExpressionসাথে expr1 এবং expr2 এ কাজ করছেন? যদি তা হয় তবে এটি আরও সহজ:

var body = Expression.AndAlso(expr1.Body, expr2.Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);

এটি একটি একক অপারেশনকে অস্বীকার করার জন্যও ভাল কাজ করে:

static Expression<Func<T, bool>> Not<T>(
    this Expression<Func<T, bool>> expr)
{
    return Expression.Lambda<Func<T, bool>>(
        Expression.Not(expr.Body), expr.Parameters[0]);
}

অন্যথায়, লিনকিউ সরবরাহকারীর উপর নির্ভর করে আপনি তাদের সাথে একত্রিত করতে সক্ষম হতে পারেন Invoke:

// OrElse is very similar...
static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> left,
    Expression<Func<T, bool>> right)
{
    var param = Expression.Parameter(typeof(T), "x");
    var body = Expression.AndAlso(
            Expression.Invoke(left, param),
            Expression.Invoke(right, param)
        );
    var lambda = Expression.Lambda<Func<T, bool>>(body, param);
    return lambda;
}

কোথাও, আমি কিছু কোড পেয়েছি যা প্রয়োজনীয়তা অপসারণের জন্য নোডগুলি প্রতিস্থাপন করে একটি এক্সপ্রেশন-ট্রি পুনরায় লিখেছে Invokeতবে এটি বেশ দীর্ঘ (এবং আমি এটি কোথায় রেখেছি তা মনে করতে পারি না))


সাধারণ সংস্করণ যা সহজতম রুটটি বেছে নেয়:

static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> expr1,
    Expression<Func<T, bool>> expr2)
{
    // need to detect whether they use the same
    // parameter instance; if not, they need fixing
    ParameterExpression param = expr1.Parameters[0];
    if (ReferenceEquals(param, expr2.Parameters[0]))
    {
        // simple version
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(expr1.Body, expr2.Body), param);
    }
    // otherwise, keep expr1 "as is" and invoke expr2
    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(
            expr1.Body,
            Expression.Invoke(expr2, param)), param);
}

.NET 4.0 থেকে শুরু করে, এমন ExpressionVisitorক্লাস রয়েছে যা আপনাকে EF নিরাপদ মত প্রকাশ তৈরি করতে দেয়।

    public static Expression<Func<T, bool>> AndAlso<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var parameter = Expression.Parameter(typeof (T));

        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
        var left = leftVisitor.Visit(expr1.Body);

        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
        var right = rightVisitor.Visit(expr2.Body);

        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(left, right), parameter);
    }



    private class ReplaceExpressionVisitor
        : ExpressionVisitor
    {
        private readonly Expression _oldValue;
        private readonly Expression _newValue;

        public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
        {
            _oldValue = oldValue;
            _newValue = newValue;
        }

        public override Expression Visit(Expression node)
        {
            if (node == _oldValue)
                return _newValue;
            return base.Visit(node);
        }
    }

আরে মার্ক, আমি আপনার প্রথম পরামর্শটি উপরের প্রথম কোড ব্লকে চেষ্টা করে দেখেছি, কিন্তু যখন আমি "ল্যাম্বদা" এক্সপ্রেশনটি <ফানক <টি, বুল >> পাস করি যেখানে কোনও পদ্ধতিতে আসে তখন আমি প্যারামিটারটি বলে ত্রুটি পাই সুযোগের বাইরে? কোন ধারণা? চিয়ার্স
অ্যান্ডি

1
+1 জেনারালাইজড সংস্করণটি কবজির মতো কাজ করে, আমি ব্যবহার করেছিলাম এবং অ্যান্ডালসোর পরিবর্তে, আমি ভেবেছিলাম লিঙ্ক টু এসকিএল এন্ডালোকে সমর্থন করে না?
মাসলো

2
: - @Maslow এখানে একটি rewriter যে ডাকা সংরক্ষণ করতে গাছ ইনলাইন পারে stackoverflow.com/questions/1717444/...
মার্ক Gravell

1
@ অ্যারন এখন তারিখটি দেখুন: .NET ফ্রেমওয়ার্ক ভিজিটর ( ExpressionVisitor) তখন আর উপস্থিত ছিল না ; আমার স্ট্যাকওভারফ্লো সম্পর্কিত একটি অনুরূপ তারিখ রয়েছে যেখানে এটি দর্শনার্থীকে ম্যানুয়ালি প্রয়োগ করে: এটি প্রচুর কোড।
মার্ক গ্র্যাভেল

1
@ মার্কগ্রাভেল, আমি আমার অভিব্যক্তিগুলি একত্রিত করার জন্য আপনার প্রথম সমাধানটি ব্যবহার করছি, এবং সত্তা ফ্রেমওয়ার্কেও সবকিছু ঠিকঠাকভাবে কাজ করছে, তবে শেষ সমাধানটি ব্যবহার করে কী কী উপকার হবে?
জননি 5

60

যৌক্তিক এক্সপ্রেশনগুলি একত্রিত করতে আপনি এক্সপ্রেশন.আরএলসো / ওড়েলস ব্যবহার করতে পারেন, তবে আপনাকে নিশ্চিত করতে হবে যে প্যারামিটার এক্সপ্রেসনগুলি একই রকম।

আমি ইএফ এবং প্রেডিকেটবিল্ডারের সাথে সমস্যায় পড়েছিলাম তাই ইনভোকের অবলম্বন না করেই নিজের তৈরি করেছিলাম, যাতে আমি এটি ব্যবহার করতে পারি:

var filterC = filterA.And(filterb);

আমার প্রিডিকেটবিল্ডারের জন্য উত্স কোড:

public static class PredicateBuilder {

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }   
}

এবং একটি ল্যাম্বডায় প্যারামিটারগুলি প্রতিস্থাপন করতে ইউটিলিটি শ্রেণি:

internal class SubstExpressionVisitor : System.Linq.Expressions.ExpressionVisitor {
        public Dictionary<Expression, Expression> subst = new Dictionary<Expression, Expression>();

        protected override Expression VisitParameter(ParameterExpression node) {
            Expression newValue;
            if (subst.TryGetValue(node, out newValue)) {
                return newValue;
            }
            return node;
        }
    }

এই সমাধানটি কেবলমাত্র আমাকেই x => x.Poperty == আরগের সাথে সম্মিলিত মান>> arg.Property2 == মান দেওয়ার অনুমতি দেয়। মেজর প্রপস, একটি সামান্য পরিসর এবং বিভ্রান্তিকর তবে এটি কাজ করে তাই আমি অভিযোগ করতে যাচ্ছি না।
কুডোস

এটি একটি দুর্দান্ত সমাধান।
অ্যারন স্টেইনব্যাক

অ্যাডাম, এটি একটি খুব বিরক্তিকর সমস্যার সমাধান করেছিলাম যা আমি শেয়ার পয়েন্ট ক্লায়েন্ট অবজেক্ট মডেলের লিনক সরবরাহকারী ব্যবহার করেছিলাম - এটি পোস্ট করার জন্য ধন্যবাদ।
ক্রিস্টোফার ম্যাকআটাকনি

এটি আমার পক্ষে কাজ করেছিল! আমি বিভিন্ন সমাধানের পাশাপাশি ভবিষ্যদ্বাণীপূর্ণ নির্মাতাকে অনুসন্ধান করেছি এবং এটি পর্যন্ত কিছুই কাজ করে নি। ধন্যবাদ!
tokyo0709

এটি একটি দুর্দান্ত কোড। কোড, কপি-পেস্ট সামঞ্জস্য করার জন্য আমি কোনও জায়গা খুঁজে পাইনি এবং এটিই হল :)
টোলগা ইভিসিমন

19

যদি আপনি সরবরাহকারী ইনভোককে সমর্থন না করেন এবং আপনাকে দুটি এক্সপ্রেশন একত্রিত করতে হবে তবে আপনি প্রথম এক্সপ্রেশনটিতে প্যারামিটার দ্বারা দ্বিতীয় এক্সপ্রেশনটিতে প্যারামিটারটি প্রতিস্থাপন করতে এক্সপ্রেসভিসিটার ব্যবহার করতে পারেন।

class ParameterUpdateVisitor : ExpressionVisitor
{
    private ParameterExpression _oldParameter;
    private ParameterExpression _newParameter;

    public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
    {
        _oldParameter = oldParameter;
        _newParameter = newParameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (object.ReferenceEquals(node, _oldParameter))
            return _newParameter;

        return base.VisitParameter(node);
    }
}

static Expression<Func<T, bool>> UpdateParameter<T>(
    Expression<Func<T, bool>> expr,
    ParameterExpression newParameter)
{
    var visitor = new ParameterUpdateVisitor(expr.Parameters[0], newParameter);
    var body = visitor.Visit(expr.Body);

    return Expression.Lambda<Func<T, bool>>(body, newParameter);
}

[TestMethod]
public void ExpressionText()
{
    string text = "test";

    Expression<Func<Coco, bool>> expr1 = p => p.Item1.Contains(text);
    Expression<Func<Coco, bool>> expr2 = q => q.Item2.Contains(text);
    Expression<Func<Coco, bool>> expr3 = UpdateParameter(expr2, expr1.Parameters[0]);

    var expr4 = Expression.Lambda<Func<Recording, bool>>(
        Expression.OrElse(expr1.Body, expr3.Body), expr1.Parameters[0]);

    var func = expr4.Compile();

    Assert.IsTrue(func(new Coco { Item1 = "caca", Item2 = "test pipi" }));
}

1
এটি আমার বিশেষ সমস্যার সমাধান করেছে যেখানে অন্যান্য সমাধানের ক্ষেত্রে একই ব্যতিক্রম ঘটে। ধন্যবাদ।
শন উইলসন

1
এটি একটি দুর্দান্ত সমাধান।
অ্যারন স্টেইনব্যাক

3

এখানে নতুন কিছু নয় তবে এই উত্তরটির সাথে এই উত্তরটি বিবাহ করলেন এবং কিছুটা রিফ্যাক্ট করলেন যাতে আমি বুঝতে পারি কী চলছে:

public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        ParameterExpression parameter1 = expr1.Parameters[0];
        var visitor = new ReplaceParameterVisitor(expr2.Parameters[0], parameter1);
        var body2WithParam1 = visitor.Visit(expr2.Body);
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, body2WithParam1), parameter1);
    }

    private class ReplaceParameterVisitor : ExpressionVisitor
    {
        private ParameterExpression _oldParameter;
        private ParameterExpression _newParameter;

        public ReplaceParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
        {
            _oldParameter = oldParameter;
            _newParameter = newParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (ReferenceEquals(node, _oldParameter))
                return _newParameter;

            return base.VisitParameter(node);
        }
    }
}

ধারণাটি উপলব্ধি করতে আমার অসুবিধা হচ্ছিল এবং আপনার কয়েকটি উত্তর উত্তর মিশ্রণ এটি আমার জন্য ক্লিক করতে সহায়তা করেছিল। ধন্যবাদ!
কেভিন এম লাপিও

2

আমার একই ফলাফল অর্জন করা দরকার, তবে আরও সাধারণ কিছু ব্যবহার করা হয়েছিল (কারণ এটি প্রকারটি জানা ছিল না)। মার্কের উত্তরের জন্য ধন্যবাদ আমি অবশেষে বুঝতে পেরেছিলাম যে আমি কী অর্জন করতে চাইছি:

    public static LambdaExpression CombineOr(Type sourceType, LambdaExpression exp, LambdaExpression newExp) 
    {
        var parameter = Expression.Parameter(sourceType);

        var leftVisitor = new ReplaceExpressionVisitor(exp.Parameters[0], parameter);
        var left = leftVisitor.Visit(exp.Body);

        var rightVisitor = new ReplaceExpressionVisitor(newExp.Parameters[0], parameter);
        var right = rightVisitor.Visit(newExp.Body);

        var delegateType = typeof(Func<,>).MakeGenericType(sourceType, typeof(bool));
        return Expression.Lambda(delegateType, Expression.Or(left, right), parameter);
    }

1

আমি প্রডিকেটবিল্ডার এবং ExpressionVisitorসমাধানগুলিতে আরও একটি উন্নতির পরামর্শ দিচ্ছি । আমি এটিকে ফোন করেছি UnifyParametersByNameএবং আপনি এটি আমার এমআইটি লাইব্রেরিতে খুঁজে পেতে পারেন: লিনকেক্সেক্সপ্রহেল্পার । এটি সালিসি ল্যাম্বদা এক্সপ্রেশনগুলির সংমিশ্রণের জন্য অনুমতি দেয়। সাধারণত ভবিষ্যদ্বাণীপূর্ণ অভিব্যক্তি সম্পর্কে প্রশ্ন জিজ্ঞাসা করা হয়, তবে এই ধারণাটি প্রক্ষেপণ অভিব্যক্তিগুলিতেও প্রসারিত।

নিম্নলিখিত কোডটি ExprAdresএমন একটি পদ্ধতি নিয়োগ করে যা ইনলাইন ল্যাম্বদা ব্যবহার করে একটি জটিল প্যারামিট্রাইজড এক্সপ্রেশন তৈরি করে। এই জটিল এক্সপ্রেশনটি কেবল একবার কোড করা হয়, এবং তারপরে পুনরায় ব্যবহৃত হয়, LinqExprHelperমিনি লাইব্রেরিটির জন্য ধন্যবাদ ।

public IQueryable<UbezpExt> UbezpFull
{
    get
    {
        System.Linq.Expressions.Expression<
            Func<UBEZPIECZONY, UBEZP_ADRES, UBEZP_ADRES, UbezpExt>> expr =
            (u, parAdrM, parAdrZ) => new UbezpExt
            {
                Ub = u,
                AdrM = parAdrM,
                AdrZ = parAdrZ,
            };

        // From here an expression builder ExprAdres is called.
        var expr2 = expr
            .ReplacePar("parAdrM", ExprAdres("M").Body)
            .ReplacePar("parAdrZ", ExprAdres("Z").Body);
        return UBEZPIECZONY.Select((Expression<Func<UBEZPIECZONY, UbezpExt>>)expr2);
    }
}

এবং এটি হ'ল সুব এক্সপ্রেসন বিল্ডিং কোড:

public static Expression<Func<UBEZPIECZONY, UBEZP_ADRES>> ExprAdres(string sTyp)
{
    return u => u.UBEZP_ADRES.Where(a => a.TYP_ADRESU == sTyp)
        .OrderByDescending(a => a.DATAOD).FirstOrDefault();
}

আমি যেটি অর্জন করার চেষ্টা করেছি তা হ'ল প্যারামাইট্রাইজড ক্যোয়ারীগুলি অনুলিপি করে কপি-পেস্ট না করে এবং ইনলাইন ল্যাম্বডাস ব্যবহার করার ক্ষমতা সহ, যা খুব সুন্দর। এই সমস্ত সহায়ক-অভিব্যক্তি স্টাফ ছাড়াই, আমি একযোগে পুরো ক্যোয়ারী তৈরি করতে বাধ্য হব।


-7

আমি মনে করি এটি ভাল কাজ করে, তাই না?

Func<T, bool> expr1 = (x => x.Att1 == "a");
Func<T, bool> expr2 = (x => x.Att2 == "b");
Func<T, bool> expr1ANDexpr2 = (x => expr1(x) && expr2(x));
Func<T, bool> expr1ORexpr2 = (x => expr1(x) || expr2(x));
Func<T, bool> NOTexpr1 = (x => !expr1(x));

1
উদাহরণস্বরূপ এটি লিনক থেকে এসকিউএল এ ব্যবহার করা যাবে না
রোমেন ভার্জিনি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.