LINQ to Dynamics CRM регистрирует записи запросов локально
Я написал запрос Linq для CRM, используя CRM-провайдер CRM 2011 RC (v5) LINQ-to-CRM. У меня есть локально объявленный список <T> который я хочу присоединиться к объекту CRM, и я хочу, чтобы запрос выполнялся на сервере CRM. Пример может помочь:
MyObject myObject = new MyObject();
List<myAccount> myAccountsList = new List<myAccount>();
myAccountsList.Add(new myAccount() {AccountNumber = "123"};
myAccountsList.Add(new myAccount() {AccountNumber = "456"};
myObject.ListOfAccounts = myAccountsList;
var accountsQuery = from ax in myObject.ListOfAccounts
join a in orgContext.CreateQuery<customAccountEntity>() on ax.AccountNumber equals a.account_number
select a;
foreach(var item in accountsQuery)
{
Console.WriteLine("Id of record retrieved: " + a.Id.ToString());
}
Приведенный выше код компилируется и выполняется, однако фильтрация записей выполняется локально после извлечения всего набора записей объектов CRM. Очевидно, что когда объект CRM содержит тысячи строк, запрос будет выполнять плохо или даже таймаут.
Я прочитал об IQueryable и IEnumerable и попытался преобразовать Список, используя метод расширения AsQueryable(), который не имел никакого эффекта. Мне нужен мой предыдущий запрос Linq для запуска SQL следующим образом:
SELECT a.*
FROM customAccountEntity AS a
WHERE a.account_number IN ('123', '456');
Или используя временную таблицу, если хотите присоединиться к нескольким полям. Как я могу это сделать?
Ответы
Ответ 1
После многих ударов по голове и исследований я решил проблему, используя Predicate Builder и LINQKit. Мне нужно создать предикат Or, используя ключи в моем локальном List <T> , затем передать предикат методу расширения Where LINQ. Важно отметить, что мне нужно вызвать метод расширения ASExpandable, открытый LINQKit. Поэтому мой код будет выглядеть так:
var predicate = PredicateBuilder.False<customAccountEntity>();
// Loop through the local List creating an Or based predicate
foreach (var item in myAccountsList)
{
string temp = item.AccountNumber;
predicate = predicate.Or (x => x.customCrmEntityAttribute == temp);
}
// The variable predicate is of type Expression<Func<customAccountEntity, bool>>
var myLinqToCrmQuery = from ax in myObject.ListOfAccounts
from cx in orgContext.CreateQuery<customAccountEntity>().AsExpandable().Where(predicate)
where ax.AccountNumber == cx.account_number
select cx;
foreach (resultItem in myLinqToCrmQuery)
{
Console.WriteLine("Account Id: " + resultItem.Id);
}
Вышеприведенный код запускает SQL-запрос на сервере CRM следующим образом:
SELECT a.*
FROM customAccountEntity AS a
WHERE a.account_number = '123' OR a.account_number = '456'
Это означает, что я могу создать предложение dynamic where во время выполнения и знать, что мой запрос будет запускать логику фильтрации на CRM SQL Server. Надеюсь, это поможет кому-то еще.
Ответ 2
Вместо воспроизведения с предикатами вы также можете просто использовать выражение объединения для фильтрации.
var myLinqToCrmQuery = from ax in myObject.ListOfAccounts
join cx in orgContext.CreateQuery<customAccountEntity> on ax.AccountNumber equals cx.account_number
select cx;
Cheers,
Лукаш
Ответ 3
Изменить: попробуйте следующее:
MyObject myObject = new MyObject();
List<myAccount> myAccountsList = new List<myAccount>();
myAccountsList.Add(new myAccount() {AccountNumber = "123"};
myAccountsList.Add(new myAccount() {AccountNumber = "456"};
myObject.ListOfAccounts = myAccountsList;
var accountNumbers = myObject.ListOfAccounts.Select(a => a.AccountNumber);
var accountsQuery = orgContext.CreateQuery<customAccountEntity>()
.Where(a => accountNumbers.Contains(a.account_number));
foreach(var item in accountsQuery)
{
Console.WriteLine("Id of record retrieved: " + a.Id.ToString());
}
Изменить: если поставщик запроса не поддерживает Содержит, создайте условие Where с несколькими OR, вы можете использовать построитель предикатов, чтобы сделать это легко