Наиболее эффективный динамический построитель запросов или предикатов в ASP.NET MVC 5 с C #, Entity Framework, SQL Server - PullRequest
0 голосов
/ 14 сентября 2018

В моем бизнес-приложении я считаю необходимым использовать список результатов, сгенерированных из Таблицы 2, с помощью которых можно запросить Таблицу 1 для получения дополнительных данных, где между Таблицей 2 и Таблицей 1 существует отношение много к одному.

Для примера рассмотрим следующее:

Скажем, в Таблице 2 содержится информация о заказе, а в этой таблице два поля - Идентификатор продукта и Общая стоимость продажи.Скажем, в Таблице 1 содержится информация о продукте, идентификатор продукта которой уникален, и более подробная информация о продукте.В этом примере я хотел бы запросить в таблице 2 список всех продаж более 1000 долларов и вернуть идентификатор продукта.Затем я хотел бы использовать этот список, чтобы запросить таблицу 1, чтобы получить более подробную информацию об этих товарах.

Для этого я обнаружил albahari PredicateBuilder, найденный здесь: http://www.albahari.com/nutshell/predicatebuilder.aspx.

Это отличное решение моей проблемы, однако я считаю, что мои наборы данных слишком велики для этого.Я получаю ошибку переполнения стека при любом количестве более 40000 предикатов.Очевидно, PredicateBuilder должен рекурсивно шагать по дереву выражений, и, поскольку размер стека ограничен, будет выдано исключение StackOverflowException.

Я провел некоторое копание, и эта проблема, включая некоторые решения, подробно описана здесь: https://kalcik.net/2014/01/05/joining-data-in-memory-with-data-in-database-table/

Чтобы преодолеть исключение StackOverflowException, по приведенной выше ссылке я реализовал следующее:

        IQueryable<Asset> query = DbContext.ProductInformation;

        IEnumerable<OrderInformation> query_order = DbContext.OrderInformation;             
        query_order = query_order.Where(x => x.SalesValue > 1000);
        query = FoundProductsWithFragments(query_order, 10);

        private static IQueryable<ProductInformation> FoundProductsWithFragments(IEnumerable<OrderInformation> productsLookupTable, int chunkSize)
    {

        var productsLookupTableFragmented = productsLookupTable.Select((productToSearch, index) =>
                                                                new { ProductToSearch = productToSearch.ProductID, Index = index })
                                                                //.Distinct()
                                                                .GroupBy(productToSearch => productToSearch.Index / chunkSize);

        System.Diagnostics.Debug.WriteLine("productsLookupTableFragmented_cnt: " + productsLookupTableFragmented.Count());


        var foundProducts = new List<ProductInformation>();
        int ii = 0;

        foreach (var productLookupTableFragmentEntry in productsLookupTableFragmented)
        {
            var productsFromLookupTable = productLookupTableFragmentEntry.Select(e => e.ProductToSearch);

            var predicate = PredicateBuilder.New<ProductInformation>();

            foreach (var productFromLookupTable in productsFromLookupTable)
            {
                predicate = predicate.Or(searchedProduct => searchedProduct.ProductID == productFromLookupTable);
                System.Diagnostics.Debug.WriteLine(ii + " employeeFromLookupTable_id: " + productFromLookupTable);
                ii++;
            }

            using (var companyDbContext = new ApplicationDbContext())
            {
                foundProducts.AddRange(companyDbContext.Assets.AsExpandable().Where(predicate).ToList());

            }

        }


        return foundProducts.AsQueryable();


    }

В настоящее время он работает, но генерирует список из 100 000 предикатов.На момент написания этой статьи моя функция 30 минут во время выполнения и все еще выполняется.

В приведенной выше ссылке Антона Калчика в контрольном примере 3 (тестовый случай 3 - использование PredicateBuilder с 10 фрагментами) время выполнения составляет всего 2 минуты с 100 000 предикатов.Я не уверен, какая огромная разница в моей реализации.

В любом случае, мой вопрос такой же - какой самый эффективный способ построить динамический запрос для описанного мной типа использования?

Любая помощь будет принята с благодарностью

...