Использование LINQ с хранимой процедурой, которая возвращает несколько экземпляров одного и того же объекта в строке - PullRequest
2 голосов
/ 14 января 2009

Наша политика разработки требует, чтобы все обращения к базе данных осуществлялись с помощью хранимых процедур, и это создает проблему при использовании LINQ.

Сценарий, обсуждаемый ниже, несколько упрощен для упрощения объяснения.

Рассмотрим базу данных с двумя таблицами.

  • Заказы (OrderID (PK), InvoiceAddressID (FK), DeliveryAddressID (FK))
  • Адреса (AddIDID (PK), Street, ZipCode)

Набор результатов, возвращаемый хранимой процедурой, должен переименовать столбцы, связанные с адресом, чтобы адреса счета и доставки отличались друг от друга.

OrderID InvAddrID DelAddrID InvStreet DelStreet InvZipCode DelZipCode
1       27        46        Main St   Back St   abc123     xyz789

Это, однако, означает, что LINQ не знает, что делать с этими столбцами в наборе результатов, поскольку они больше не соответствуют именам свойств в объекте Address.

Огорчает то, что в этом нет никакого способа определить, какие столбцы результирующего набора соответствуют каким свойствам сущности, даже если можно (в определенной степени) сопоставить свойства сущности с параметрами хранимой процедуры для вставки / операции обновления.

У кого-нибудь еще была такая же проблема?

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

Ответы [ 5 ]

3 голосов
/ 14 января 2009

Рассматривали ли вы создание представления, подобного приведенному ниже, для хранимой процедуры на выбор? Это добавит сложности, но позволит LINQ видеть сущность так, как вы хотели.

Create view OrderAddress as 
Select o.OrderID 
,i.AddressID as InvID
,d.AddressID as DelID
...
from Orders o
left join Addresses  i
on o.InvAddressID= i.AddressID
left join Addresses d
on o.DelAddressID = i.AddressID
2 голосов
/ 21 февраля 2009

LINQ немного суетлив относительно запросов данных; он хочет, чтобы схема соответствовала. Я подозреваю, что вам придется вернуть это обратно в автоматически сгенерированный тип и выполнить сопоставление с вашим типом сущности после в LINQ с объектами (т.е. после AsEnumerable () или подобного) - как это не вам не нужно создавать экземпляры сопоставленных объектов вручную внутри запроса.

На самом деле, я бы рекомендовал оспаривать требование в одном отношении: вместо SP, рассмотрите возможность использования UDF для запроса данных; они работают аналогичным образом с точки зрения принадлежности к базе данных, но они компонуются на сервере (подкачка страниц, сортировка, объединение и т. д.).

(это немного случайно - бери с щепоткой соли)

UDF могут быть связаны с типами сущностей, если схема соответствует, поэтому другой вариант (я не пробовал) будет иметь GetAddress (id) udf и «основной» udf и присоединиться к ним:

var qry = from row in ctx.MainUdf(id)
          select new {
             Order = ctx.GetOrder(row.OrderId),
             InvoiceAddress = ctx.GetAddress(row.InvoiceAddressId),
             DeliveryAddress = ctx.GetAddress(row.DeliveryAddressId)) };

(где udf просто возвращает идентификаторы - на самом деле, у вас может быть соединение с другими udf, что делает его еще хуже).

или что-то - может быть слишком грязно для серьезного рассмотрения, однако.

1 голос
/ 19 июня 2009

Я думаю, я понимаю, что вы после, но я мог бы отключиться ...

Если вы макетируете классы в DBML (щелкните правой кнопкой мыши -> новый -> класс), которые имеют ту же структуру, что и ваши исходные таблицы, вы можете просто создать новые объекты на основе того, что читается из хранимой процедуры. Используя LINQ для объектов, вы все равно можете запросить свой выбор. Это больше кода, но это не так сложно сделать. Например, смоделируйте ваш DBML так:

Обратите внимание на ассоциации http://geeksharp.com/screens/orders-dbml.png

Убедитесь, что вы обратили внимание на ассоциации, которые я добавил. Вы можете развернуть «Родительское свойство» и изменить имя этих ассоциаций на «InvoiceAddress» и «DeliveryAddress». Я также изменил имена дочерних свойств на «InvoiceOrders» и «DeliveryOrders» соответственно. Обратите внимание на хранимую процедуру вверху под названием "usp_GetOrders." Теперь с небольшим количеством кода вы можете сопоставить столбцы вручную. Я знаю, что это не идеально, особенно если хранимый процесс не отображает каждого члена каждой таблицы, но может приблизить вас:

public List<Order> GetOrders()
{
    // our DBML classes
    List<Order> dbOrders = new List<Order>();

    using (OrderSystemDataContext db = new OrderSystemDataContext())
    {
        // call stored proc
        var spOrders = db.usp_GetOrders();

        foreach (var spOrder in spOrders)
        {
            Order ord = new Order();
            Address invAddr = new Address();
            Address delAddr = new Address();

            // set all the properties
            ord.OrderID = spOrder.OrderID;

            // add the invoice address
            invAddr.AddressID = spOrder.InvAddrID;
            invAddr.Street = spOrder.InvStreet;
            invAddr.ZipCode = spOrder.InvZipCode;
            ord.InvoiceAddress = invAddr;

            // add the delivery address
            delAddr.AddressID = spOrder.DelAddrID;
            delAddr.Street = spOrder.DelStreet;
            delAddr.ZipCode = spOrder.DelZipCode;
            ord.DeliveryAddress = delAddr;

            // add to the collection
            dbOrders.Add(ord);
        }
    }

    // at this point I have a List of orders I can query...
    return dbOrders;
}

Опять же, я понимаю, что это кажется громоздким, но я думаю, что конечный результат стоит нескольких дополнительных строк кода.

1 голос
/ 18 июня 2009

Если вы точно знаете, какие столбцы будет включать ваш результирующий набор, вы сможете создать новый тип сущности, который будет иметь свойства для каждого столбца в результирующем наборе. Например, вместо того, чтобы пытаться упаковать данные в Order, вы можете упаковать их в OrderWithAddresses, который имеет структуру, ожидаемую вашей хранимой процедурой. Если вы используете LINQ to Entities, вы даже можете указать в своем файле .edmx, что OrderWithAddresses - это ордер с двумя дополнительными свойствами. В LINQ to SQL вам придется указывать все столбцы, как если бы это был совершенно не связанный тип данных.

Если ваши столбцы генерируются динамически с помощью хранимой процедуры, вам нужно будет попробовать другой подход: создать новую хранимую процедуру, которая извлекает данные только из таблицы Orders, и ту, которая извлекает данные только из таблицы адресов. Настройте отображение LINQ для использования этих хранимых процедур. (Конечно, единственная причина, по которой вы используете хранимые процедуры, - это соблюдение политики вашей компании). Затем используйте LINQ, чтобы объединить эти данные. Он должен быть лишь немного менее эффективным, но он будет более адекватно отражать фактическую структуру ваших данных, что, я думаю, является лучшей практикой программирования.

0 голосов
/ 18 июня 2009

это вообще не очень эффективно, но если ничего не помогает, вы можете попробовать сделать два вызова процедур из приложения: один, чтобы получить адрес счета, а затем другой, чтобы получить адрес доставки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...