1

In EF Core I have a list of composite Id's, and I then want to have of those ids from the database.

var crits = new List<MyCrit>()
{
    new MyCrit() {Key1 = "A", Key2 = 3},
    new MyCrit() {Key1 = "B", Key2 = 4}
};

It should end up with a SQL query something like this:

select * 
from MyTable 
where (Key1 = "A" and Key2 = 3) or (Key1 = "B" and Key2 = 4)

I have configured the table to have correct setup, but I cannot get the OR.

Here is my code:

var query = _db.MyTable.AsQueryable();

foreach (var crit in crits)
{
    query = query.Where(m => m.Key1 == crit.Key1 && m.Key2 == crit.Key2);
}

Unfortunately, that results in this SQL statement:

select * 
from MyTable 
where (Key1 = "A" and Key2 = 3) and (Key1 = "B" and Key2 = 4)

I can't figure out how to add inside the loop so it becomes OR.

4
  • Change && to || Commented Mar 29, 2022 at 11:40
  • Probably simplest to make a single Where and join the expressions together yourself. Commented Mar 29, 2022 at 11:42
  • My first guess is that in your Foreach loop, you update the "query" variable, based on the old value of query (query = query.Where[....]) on every iteration. I don't think it is a wanted behavior here. To avoid that, you can try to define a base query, immutable, and then work with a tempQuery variable (tempQuery = constQuery.Where[...]) Commented Mar 29, 2022 at 11:53
  • Use this function FilterByItems It will generate OR for each record. Commented Mar 29, 2022 at 12:05

3 Answers 3

2

Use FilterByItems extension and you can simplify your query to the following:

var query = _db.MyTable
    .FilterByItems(crits, (m, crit) => m.Key1 == crit.Key1 && m.Key2 == crit.Key2, true);
Sign up to request clarification or add additional context in comments.

Comments

1

I have some hope this should work - just Concat the queries together. It should only result in a single query against the database.

using System.Collections.Generic;
using System.Linq;
                    
public class Program
{
    public static void Main()
    {
        var crits = new List<MyCrit>()
        {
            new MyCrit() {Key1 = "A", Key2 = 3},
            new MyCrit() {Key1 = "A", Key2 = 4}
        };
        
        // _db.Table.AsQueryable()
        var table = Enumerable.Empty<DbValue>().AsQueryable();
        
        // Could use a foreach over crits with an initial value of Enumerable.Empty<DbValue>().AsQueryable()
        var filtered = crits.Select(x => table.Where(y => y.Key1 == x.Key1 && y.Key2 == x.Key2))
            .Aggregate(Enumerable.Empty<DbValue>().AsQueryable(), (x, y) => x.Concat(y));
    }
}

public class MyCrit
{
    public string Key1;
    public int Key2;
}

public class DbValue
{
    public string Key1;
    public int Key2;
    public string OtherData;
}

1 Comment

I tried this and got this error: System.ArgumentException: must be reducible node
0

Maybe something like this? (I wrote it from memory and fast so something can work wrong...)

UPDATE Now I could check it and fix and now should work

Expression CreateBasicExpression(ParameterExpression parameterExpression1, string crit)
            {
                var rightPart = Expression.Equal(Expression.Property(parameterExpression1, "Name"), Expression.Constant(crit));
                var leftPart = Expression.Equal(Expression.Property(parameterExpression1, "Surname"), Expression.Constant(crit));
                return Expression.And(leftPart, rightPart);
            }

            var parameterExpression = Expression.Parameter(typeof(MyTable));

            Expression basicExpression = null;

            var crits = new List<string>
            {
                "test1",
                "test2"
            };

            foreach (var crit in crits)
            {
                if (basicExpression is null)
                {

                    basicExpression = CreateBasicExpression(parameterExpression, crit);
                }
                else
                {
                    basicExpression = Expression.Or(basicExpression, CreateBasicExpression(parameterExpression, crit));
                }
            }

            var resultExpression = Expression.Lambda(basicExpression, parameterExpression);

            var castedExpression = (Expression<Func<MyTable, bool>>)resultExpression

9 Comments

I think maybe this is right, and the only thing I can't implement is the last line where the where I maybe need to cast my basic expression back to a Func<MyObj, bool> or something
You should be able to cast it back to IQueryable<MyObj> - maybe try query = query.Where(exp) or explicit cast?
Thanks, that worked, but then Runtime I got this error: System.InvalidOperationException: The binary operator Or is not defined for the types 'System.Func2[MyTable, Boolean]' and 'System.Func2[MyTable, Boolean]'
can you show how did you do it?
Pls looks now on it
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.