Combining two expressions (Expression<Func<T, bool>>)
Asked Answered
A

10

323

I have two expressions of type Expression<Func<T, bool>> and I want to take to OR, AND or NOT of these and get a new expression of the same type

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
Air answered 19/1, 2009 at 11:29 Comment(0)
C
444

Well, you can use Expression.AndAlso / OrElse etc to combine logical expressions, but the problem is the parameters; are you working with the same ParameterExpression in expr1 and expr2? If so, it is easier:

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

This also works well to negate a single operation:

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

Otherwise, depending on the LINQ provider, you might be able to combine them with 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;
}

Somewhere, I have got some code that re-writes an expression-tree replacing nodes to remove the need for Invoke, but it is quite lengthy (and I can't remember where I left it...)


Generalized version that picks the simplest route:

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

Starting from .NET 4.0, there is the ExpressionVisitor class which allows you to build expressions that are EF safe.

    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);
        }
    }
Catchword answered 19/1, 2009 at 11:32 Comment(22)
Hey Marc, I tried out your first suggestion, in the your first code block above, but when I pass in the "lambda" expression<func<T,bool>> result in a Where method, I get an error saying the parameter is out of scope? any idea? cheersGabel
@Andy - yes (see the first sentence) - you will get this if you are using different parameter instances in the two versions... I'll update with another option (but which doesn't work for all providers; LINQ-to-Objects and LINQ-to-SQL will be fine, but EF won't be...)Catchword
D'oh! I've already covered this in the second version... it can be simplified a little bit, though (will update)Catchword
+1 the generalized version works like a charm, I used And instead of andalso, I thought linq to sql doesn't support andalso?Transcendent
you are a legend. I just tried it and the generalised version works excellently! (I tested it with NH 3.0 alpha 2)Coper
hmm getting the invoke not supported for EF4, is there a version that would work of yours for this marc?Transcendent
@Transcendent - here's a rewriter that can inline the trees to save Invoke: #1717944Catchword
@Marc - thanks a ton, I imagine there are other places in the code where this was needed, but it looks so far like just chaining .Where(cond1).FirstOrDefault(cond2) is working in the place that brought me back here, not sure about scalability or performance.Transcendent
@Aron Hi, I tried Your update with ExpressionVisitor and I get Expression of type 'System.Func2[(TSource),System.Boolean]' cannot be used for return type 'System.Boolean` what could be cause for that?Plunk
hi @MarcGravell how to do it when we use two different 'ParameterExpression' please it would be really uselful for mePollinosis
@Nishanth just use a Dictionary<Expression,Expression> and some TryGetValue and it should work for any number of swapsCatchword
Thanks, @MarcGravell but, I have followed the tuto codeproject.com/Tips/582450/… But wat i need is to be able to query the child entities also.. is it possible? what I can do now is db.Jobs.Include(j => j.SubCategory).Where(MyExpressionBuilder.Build<Job>(filters)).ToList(); what i want to do : db.Jobs.Include(j => j.SubCategory). .Where(MyExpressionBuilder.Build<Job>(filters) && MyExpressionBuilder.Build<Subcategory>(filters2)).ToList(); plsConsultTheArticle forDetails, any idea please?Pollinosis
@MarcGravell Awesome piece of code! It's a bit faster to use expr1.Parameters[0] as the parameterexpression though, then you don't have to visit the left expression :-)Gabrielson
@MarkGravell, I'm using your first solution to combine my expressions, and everything is working fine even in entityframework, So what would the benefits of using the last solution be?Skewer
@johnny5 the first (simpler) solution should only work if the parameters within the lambda bodies are the same parameters. given x => x > 5 and y => y < 100 you need to make z => z > 5 && z < 100, not z => x > 5 && y < 100. if x and y are already the same parameter somehow, then that's no problem. if they aren't then one expression needs to be rewritten with the parameter of the other, (or both rewritten with a new one), which is what the ReplaceExpressionVisitor allows. (note that the parameters need to be the same objects not just have the same names).Lamrouex
@DaveCousineau Yeah I found this out that hard when when it came back to bite me. But just to add to that point event if you have x => x > 5 and separate expression x => x < 100, those x's are local to the expression and if you try to use them even you will still get an exceptionSkewer
Note that you can optimize this somewhat: you don't need to rewrite both expr1 and expr2, but you can instead rewrite just expr2 and replace expr2.Parameters[0] with expr1.Parameters[0], leaving expr1 as-isRubrician
@MarcGravell Thank you for this. Do you know if the first code block (2 lines) is EF Core safe? It's reporting at compile time that it's not translatable, but I can't see why.Mcmichael
@Mcmichael because parsing expression trees is hard, so sometimes we need to help them out; try the expression-visitor version at the bottomCatchword
Is there some way to us this EF-friendly implementation in a way to nest the boolean logic? If I had expressions A, B, C, D, and E, if I wanted to build this in such a way to create (A && B) || (C && D) || E, could I do it? Note: I've already built the equivalent for OrElse as well, but I'm just not sure how to control the nesting here with the boolean logic.Giovannigip
@Giovannigip "yes" - this is Expression.OrElse(Expression.AndAlso(A, B), Expression.AndAlso(C, D))Catchword
@MarcGravell awesome, thank you so much and for being so fast to respond!!Giovannigip
W
91

You can use Expression.AndAlso / OrElse to combine logical expressions, but you have to make sure the ParameterExpressions are the same.

I was having trouble with EF and the PredicateBuilder so I made my own without resorting to Invoke, that I could use like this:

var filterC = filterA.And(filterb);

Source code for my PredicateBuilder:

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

And the utility class to substitute the parameters in a lambda:

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;
        }
    }
Warrin answered 19/9, 2012 at 14:53 Comment(5)
This solution was the only one that allowed me to have x => x.Property == Value combined with arg => arg.Property2 == Value. Major props, a little terse and confusing but it works so I'm not going to complain. Kudos Adam :-)Chinkapin
This is a great solution.Endocentric
Adam, this solved a very annoying problem I was having using the SharePoint Client Object model's Linq provider - thanks for posting it.Digraph
This worked for me! I had searched for a variety of solutions as well as predicate builder and nothing worked until this. Thank you!Garek
This is a wonderful piece of code. I couldn't find a place to adjust the code, copy-paste and that's it :)Raspberry
O
23

If you provider does not support Invoke and you need to combine two expression, you can use an ExpressionVisitor to replace the parameter in the second expression by the parameter in the first expression.

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" }));
}
Optometry answered 15/12, 2011 at 15:36 Comment(2)
This solved my particular problem where the other solution resulted in the same exception. Thanks.Fruity
This is a great solution.Endocentric
T
15

Nothing new here but married this answer with this answer and slightly refactored it so that even I understand what's going on:

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);
        }
    }
}
Tsingyuan answered 5/2, 2020 at 22:30 Comment(1)
I was having difficulty grasping the concept, and your melding of a couple other answers helped it click for me. Thanks!Trilbie
A
12

I combined some beautiful answers here to make it possible to easily support more Expression operators.

This is based on the answer of @Dejan but now it's quite easy to add the OR as well. I chose not to make the Combine function public, but you could do that to be even more flexible.

public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> leftExpression,
        Expression<Func<T, bool>> rightExpression) =>
        Combine(leftExpression, rightExpression, Expression.AndAlso);

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> leftExpression,
        Expression<Func<T, bool>> rightExpression) =>
        Combine(leftExpression, rightExpression, Expression.Or);

    public static Expression<Func<T, bool>> Combine<T>(Expression<Func<T, bool>> leftExpression, Expression<Func<T, bool>> rightExpression, Func<Expression, Expression, BinaryExpression> combineOperator)
    {
        var leftParameter = leftExpression.Parameters[0];
        var rightParameter = rightExpression.Parameters[0];

        var visitor = new ReplaceParameterVisitor(rightParameter, leftParameter);

        var leftBody = leftExpression.Body;
        var rightBody = visitor.Visit(rightExpression.Body);

        return Expression.Lambda<Func<T, bool>>(combineOperator(leftBody, rightBody), leftParameter);
    }

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

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

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return ReferenceEquals(node, _oldParameter) ? _newParameter : base.VisitParameter(node);
        }
    }
}

Usage is not changed and still like this:

Expression<Func<Result, bool>> noFilterExpression = item => filters == null;

Expression<Func<Result, bool>> laptopFilterExpression = item => item.x == ...
Expression<Func<Result, bool>> dateFilterExpression = item => item.y == ...

var combinedFilterExpression = noFilterExpression.Or(laptopFilterExpression.AndAlso(dateFilterExpression));
    
efQuery.Where(combinedFilterExpression);

(This is an example based on my actual code, but read it as pseudo-code)

Abramabramo answered 23/2, 2021 at 15:33 Comment(1)
thanks, I integrated it to my code, but I didn't try if it works or not :)Fungoid
B
4

I needed to achieve the same results, but using something more generic (as the type was not known). Thanks to marc's answer I finally figured out what I was trying to achieve:

    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);
    }
Bolte answered 1/6, 2017 at 12:48 Comment(0)
M
4
using System;
using System.Linq.Expressions;

namespace Extensions
{


    public class Example
    {
        //How to use it
        public static void Main()
        {
            Expression<Func<string, bool>> expression1 = exp => true;
            Expression<Func<string, bool>> expression2 = exp => false;

            Expression<Func<string, bool>> expression3 = ExpressionExtensions.AndAlso(expression1, expression2);
            Expression<Func<string, bool>> expression4 = ExpressionExtensions.OrElse(expression1, expression2);

            Expression<Func<string, bool>> expression = ExpressionExtensions.AndAlso(expression3, expression4);
        }
    }


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

        public static Expression<Func<T, bool>> OrElse<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.OrElse(expr1.Body, body2WithParam1), parameter1);
        }

        private class ReplaceParameterVisitor : ExpressionVisitor
        {
            private readonly ParameterExpression _oldParameter;
            private readonly 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);
            }
        }
    }
}
Maidenhood answered 14/9, 2022 at 13:45 Comment(1)
I use this extension class to combine where conditions, like AndAlso or OrElseMaidenhood
R
3

I suggest one more improvement to PredicateBuilder and ExpressionVisitor solutions. I called it UnifyParametersByName and you can find it in MIT library of mine: LinqExprHelper. It allows for combining arbitary lambda expressions. Usually the questions are asked about predicate expression, but this idea extends to projection expressions as well.

The following code employs a method ExprAdres which creates a complicated parametrized expression, using inline lambda. This complicated expression is coded only once, and then reused, thanks to the LinqExprHelper mini-library.

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

And this is the subexpression building code:

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

What I tried to achieve was to perform parametrized queries without need to copy-paste and with ability to use inline lambdas, which are so pretty. Without all these helper-expression stuff, I would be forced to create whole query in one go.

Ruggles answered 16/10, 2016 at 20:46 Comment(0)
P
1

One more solution (with no ExpressionVisitor) via one method and small enum to identity expression operation type

private static Expression<Func<T, bool>> BindExpressions<T>(ExpressionOperationType operationType, Expression<Func<T, bool>>[] expressionPredicates)
{
    var filterExpressionPredicate = expressionPredicates.FirstOrDefault() ?? (x => false);

    if (expressionPredicates.Length > 1)
        for (int i = 1; i < expressionPredicates.Length; i++)
        {
            var expressionBody = Expression.Invoke(expressionPredicates[i], filterExpressionPredicate?.Parameters);
            var handledExpressionUnits = operationType switch
            {
                ExpressionOperationType.AndAlso => Expression.AndAlso(filterExpressionPredicate.Body, expressionBody),
                _ => Expression.OrElse(filterExpressionPredicate.Body, expressionBody),
            };

            filterExpressionPredicate = Expression.Lambda<Func<T, bool>>(handledExpressionUnits, filterExpressionPredicate.Parameters);
        }

    return filterExpressionPredicate;
}
        
enum ExpressionOperationType
{
    AndAlso = 0,
    OrElse = 1
}

For instance: We have a model AuditLog

public class AuditLog
{
    public Guid Id { get; set; }

    public string OldValues { get; set; }

    public string NewValues { get; set; }

    public DateTime Timestamp { get; set; }
}

And we want to build specific query: search all audit records with key words "oranges", "cars", "birds" within the boundaries of date (Timestamp)

public IQueryable<AuditLog> BuildQuery()
{
    var query = _context.AuditLogs.AsNoTracking();

    var commonFilterList = new List<Expression<Func<AuditLog, bool>>>();
    commonFilterList.Add(x => x.Timestamp >= DateTime.Now);
    commonFilterList.Add(x => x.Timestamp <= DateTime.Now.AddDays(1));
    
    //real world such simple filter case I would use way like:
    //query = query
    //    .Where(x => x.Timestamp >= DateTime.Now)
    //    .Where(x => x.Timestamp <= DateTime.Now.AddDays(1));
    //but this point we keep the example

    //using AndAlso
    query = query.Where(BindExpressions(ExpressionOperationType.AndAlso, commonFilterList.ToArray()));

    //at this point we look at more useful example of using BindExpressions implementation via OrElse expression operation type
    var specificFilterList = new List<Expression<Func<AuditLog, bool>>>();

    var keyWordsToSearch = new List<string>() { "oranges", "cars", "birds" };
    
    foreach (var keyWord in keyWordsToSearch)
    {
        //real world we would to use EF.Functions.Contains / EF.Functions.FreeText statements <- but it is question another scope
        specificFilterList.Add(x => EF.Functions.Like(x.NewValues, $"%{keyWord}%"));
        specificFilterList.Add(x => EF.Functions.Like(x.OldValues, $"%{keyWord}%"));
    }

    //using OrElse
    query = query.Where(BindExpressions(ExpressionOperationType.OrElse, specificFilterList.ToArray()));

    //as result we get commonFilterList AND specificFilterList
    return query;
}
Prudential answered 16/2, 2023 at 10:49 Comment(3)
Nice trick to remove the necessity of a visitor! You could prevent some nullable type issues by using var filterExpressionPredicate = expressionPredicates.FirstOrDefault() ?? (Expression<Func<T, bool>>)(t => false); and a switch expression rather than a switch statement, so handledExpressionUnits can't be null.Vernice
@GertArnold I have refactored the BindExpressions implementation according to your recommendations. Now it is more lightweight and readable. Thank you!Prudential
Just a note: Expression.Invoke can't translate to SQL. If you need to work with Linq to SQL, the visitor pattern is the way to go.Unreserved
C
-12

I think this works fine, isn't it ?

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));
Cyclopentane answered 13/11, 2015 at 15:11 Comment(1)
this cannot be used in Linq to SQL for instanceEmulate

© 2022 - 2024 — McMap. All rights reserved.