Выберите процент записей с помощью CRM 2011 Dynamic Entity.

Я разрабатываю сервис, который использует данные CRM 2011 через динамические объекты (например, Microsoft.Xrm.Sdk.Entity, метод позднего связывания). Я намеренно не использую метод Xrm.cs (ранняя привязка), пытаясь сохранить общее решение.

Кроме того, я хочу избежать прямого подключения к базе данных CRM (например, EDMX), поскольку это помешает использованию моего решения для размещенной CRM (например, без прямого доступа к БД).

У меня есть следующее (упрощенное) требование, я действительно борюсь с критериями выбора:

случайные 7% записей должны быть выбраны (и обновлены).

В SQL критерии выбора были бы относительно просты — я знаю, как выбрать случайный процент записей. Что-то типа:

SELECT TOP 7 PERCENT * FROM
(
    SELECT TOP 1000 NEWID() AS Foo, [someColumns]
    FROM [someTable]
)
AS Bar ORDER BY Bar.Foo ASC

Это работает отлично. Я понимаю, что эквивалент LINQ выглядит примерно так:

from e in someEntities
orderby Guid.NewGuid()
select e;

Однако есть проблема: я не знаю, как использовать LINQ с динамическими объектами CRM 2011 — вместо этого они настаивают на использовании либо некоторых ограничительных классов/синтаксиса QueryExpression, либо fetchXML, как показано на эта страница (MSDN).

Я определил следующие варианты выполнения этого требования:

  1. Используя динамические объекты, верните весь набор записей в список, а затем просто выберите случайный выбор по индексу. Однако это требует возврата до 10 000 записей через службу передачи данных в Интернете, что может быть медленным, небезопасным и т. д.

  2. Используйте оператор fetchXML. К сожалению, я не знаю fetchXML, поэтому не знаю, можно ли делать такие вещи, как COUNT, TOP, PERCENT или NEWID().

  3. Используйте Xrm.cs и LINQ, хранимую процедуру или представление SQL. Все эти варианты означают привязку решения либо к прямому подключению к базе данных, либо к раннему связыванию, что нежелательно.

  4. Скажите клиенту «нет».

Любой совет будет принят с благодарностью! Может ли fetchXML выполнить этот запрос? Есть лучший способ сделать это?


person Alec    schedule 15.09.2011    source источник


Ответы (2)


FetchXML не поддерживает это, поэтому у вас может быть либо 1, либо 3. И вы правы, 3 будет работать только в локальной версии, поскольку вы не можете напрямую подключиться к SQL с продуктом CRM Online. Тем не менее, это тот вариант, который я бы выбрал, если только вы не уверены, что клиент перейдет на CRM Online. Если вы должны использовать 1, вы можете, по крайней мере, ограничить возвращаемые столбцы только GUID записи, чтобы уменьшить размер полезной нагрузки. Затем, когда вы выбираете свои случайные записи, просто получите их дополнительные столбцы, если это необходимо (конечно, это может оказаться медленнее из-за «болтливости» в зависимости от того, сколько случайных записей вы имеете дело).

person Josh Painter    schedule 15.09.2011
comment
Я думаю, что создатель требований непреклонен в том, что решение применимо к размещенной среде, поэтому я верну список идентификаторов и рандомизирую их. Я проголосовал, отмечу как ответ, если этот метод окажется успешным. Спасибо :) - person Alec; 16.09.2011
comment
Я смог сделать эту работу, используя метод, описанный выше; возврат полного набора только в виде списка идентификаторов, а затем выбор полных данных после выполнения случайного выбора в памяти. Итак, я поставлю это как ответ, потому что он действительно работает, однако я бы порекомендовал всем, у кого есть аналогичные требования, также прочитать информацию в ответе джемнапа; Я пока не знаю, какое решение более эффективно. - person Alec; 19.09.2011

На данный момент Dynamics CRM 2011 не может предоставить вам ту степень мощности запросов, которую могут предоставить SQL и другие поставщики LINQ, поэтому я действительно верю, что вы захотите сказать клиенту «нет» и перейти на локальную версию, если он/она хочет такой гибкости.

С учетом сказанного, вариант метода № 1 заключается в том, чтобы вместо того, чтобы извлекать все строки сразу, а затем выбирать свой случайный набор, извлекать случайный набор из объекта по одной строке за раз, пока вы не получите нужное количество строк. Минус этого метода в том, что вместо одного обращения к БД их много, что замедляет общую скорость извлечения. POC ниже.

Что касается № 2, я считаю, что можно с некоторой степенью успеха обработать весь ваш запрос, используя fetchXml. Фактически, это единственный способ получить агрегированные данные использует fetchXml, а также поддерживает пейджинг.

Что касается № 3, на данный момент лучше всего использовать собственный SQL, чтобы получить все, что вы хотите от ваших данных, но несмотря на это, в то время как поставщик LINQ ограничен, намного проще перенести операторы SQL на LINQ, чем на fetchXML, и он поддерживает позднее связывание/динамические сущности.

//create a list of random numbers
List<int> randomNumbers = new List<int>();

//declare a percentage of records you'd like to retrieve
double pctg = 0.07;

//use FetchXML to count the # of rows in the table
string fetchXml = @"<fetch aggregate='true'>
<entity name='salesorder'>
<attribute name='salesorderid' aggregate='count' alias='countIds' distinct='false' />
</entity>
</fetch>";
EntityCollection result = _service.RetrieveMultiple(new FetchExpression(fetchXml));
int rowCount = int.Parse(result.Entities[0].FormattedValues["countIds"].Replace(",", ""));

//initalize the random number list for paging
for (int i = 0; i < Math.Ceiling(pctg * rowCount); i++)
{
    randomNumbers.Add((new Random(unchecked((int)(DateTime.Now.Ticks >> i)))).Next(rowCount - 1));
}
randomNumbers.Sort();

//page through the rows one at a time until you have the number of rows you want
using (OrganizationServiceContext osc = new OrganizationServiceContext(_service))
{
    foreach (int r in randomNumbers)
    {
        foreach (var er in (from c in osc.CreateQuery("salesorder")
                            //not especially useful to use the orderby option as you can only order by entity attributes
                            //orderby c.GetAttributeValue<string>("name")
                            select new
                            {
                                name = c.GetAttributeValue<string>("name")
                            }).Skip(r).Take(1))
        {
            Console.WriteLine(er.name);
        }

    }
}
person Peter Majeed    schedule 17.09.2011
comment
Я не думал об использовании файла cookie подкачки - отличная идея! - person Josh Painter; 17.09.2011
comment
Некоторая очень интересная информация в этом ответе, я, конечно, не знал, что LINQ можно использовать с динамическими объектами так, как показано в вашей ссылке MSDN. FetchXML может быть лучшим способом сделать это, мне придется поэкспериментировать, чтобы увидеть, как он сравнивает производительность с другим предложенным методом. Большое спасибо за ответ. - person Alec; 19.09.2011