Afflatus
Afflatus

Reputation: 943

Dynamic joins in Linq according to the Select & Where Clause

I have a list on my UI for which the user can customize the columns to be displayed. The data for this list is being fetch from a Linq query with 10 joins which is considerably expensive. I am using Entity Framework Core 2. I would like to see if there's a way to dynamically add the joins to the query according to the specific sets of columns that user has chosen to be displayed on the list OR the filtering that has been applied to the list on the client.

For example take following query:

SELECT a.Id, a.Code, a.Sector, ad.City, ad.Country, u.FullName
FROM Accounts a
INNER JOIN Addresses ad on ad.Account = a.Id
INNER JOIN Users u ON u.Id = a.AccountManager
WHERE ad.Country = 'NL'

And this is my Linq

public IEnumerable<Account> Get(QueryClause<Account> queryClause, out ForGetCollectionOptions forGetCollectionOptions)
{
    using (_dbContext)
    {
        var result = (
            from account in _dbContext.Accounts

            join address in _dbContext.Addresses on account.ID equals address.Account into address_
            from address__ in address_.DefaultIfEmpty()

            join user in _dbContext.Users on account.AccountManager equals user.Id into user_
            from user__ in user_.DefaultIfEmpty()

            select new Account
            {
                Id = account.ID,
                Code = account.Code.Trim(),
                Sector = account.Sector.Trim(),
                City = address__.City,
                Country = address__.Country,
                AccountManager = user__.FullName
            }
        );

        //Filtering
        ApplyFilterClause(ref result, queryClause.FilterClause);
    }
    return result.toList();
}

If the user choose to only see the columns Code and Sector of each account, I wouldn't need to include the joins to the addresses nor to the users

If the user choose to only see the columns code and sector and filter based on country, I would only need to include the join to addresses but I wouldn't need to include the join to the users table.

Is there a generic way that I could implement this logic? For example if I were to send the parameter to my API such as ...&Select="Code,City" I would exclude the joins to Addresses and Users

Upvotes: 0

Views: 1175

Answers (1)

FractalCode
FractalCode

Reputation: 380

If you have bad performance with the joins you just have to make sure that the condition is false when you don't need the information of the table.

For example, if you don't need users information you can do something like this:

DECLARE  @UsersNeeded bit=0;
SELECT * FROM Accounts a 
LEFT JOIN Addresses ad on ad.Account = a.Id
LEFT JOIN Users u ON 
@UsersNeeded=1 and u.Id = a.AccountManager
WHERE ad.Country = 'NL'

Linq will be something like this:

var usersNeeded = false;
    from account in _dbContext.Accounts

    join address in _dbContext.Addresses on account.ID equals address.Account into address_
    from address__ in address_.DefaultIfEmpty()

    join user in _dbContext.Users on 
    usersNeeded equals false &&
    account.AccountManager equals user.Id into user_
    from user__ in user_.DefaultIfEmpty();

You can make a list with the tables that you need (or you don't need) and add an extra condition on the join clause.

Upvotes: 1

Related Questions