Отображение вычисленных свойств в Linq-to-SQL для актуализации операторов SQL - PullRequest
2 голосов
/ 10 октября 2008

Я создал некоторые дополнительные функции в своих классах Linq-to-SQL, чтобы упростить процесс разработки приложений. Например, я определил свойство, которое извлекает активные контракты из списка контрактов. Однако, если я пытаюсь использовать это свойство в лямбда-выражении или вообще в запросе, оно либо выдает исключение, что нет никакого оператора SQL, соответствующего этому свойству, либо генерирует один запрос на элемент (= много обращений к серверу).

Сами запросы не слишком сложны, например:

var activeContracts = customer.Contracts.Where(w => w.ContractEndDate == null);

В то время как я хотел бы, чтобы это читалось как:

var activeContracts = customer.ActiveContracts;

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

Есть ли способ указать для свойства, какой SQL он должен генерировать. Или есть способ убедиться, что его можно использовать в запросе, как показано ниже?

var singleContractCustomers = db.Customers.Where(w => w.ActiveContracts.Count() == 1);

Ответы [ 3 ]

3 голосов
/ 10 октября 2008

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

Тем не менее, я подозреваю, что вы могли бы немного разбить его. Попробуйте добавить (клиенту):

    public static Expression<Func<Customer, bool>> HasActiveContract
    {
        get { return cust => cust.Contracts.Count() == 1; }
    }

Тогда вы сможете использовать:

    var filtered = db.Customers.Where(Customer.HasActiveContract);

Очевидно, что его сложно запустить (отсюда), чтобы увидеть, с каким TSQL он подходит, но я был бы удивлен, если бы он делал обходы; Я ожидаю, что сделать COUNT () в TSQL. В качестве основного запроса вы также должны иметь возможность обернуть это:

    public IQueryable<Customer> CustomersWithActiveContract
    {
        get { return Customers.Where(Customer.HasActiveContract); }
    }

Работает ли что-нибудь из этого?

1 голос
/ 15 ноября 2012

Другим вариантом является использование библиотеки Microsoft.Linq.Translations . Это позволит вам определить вашу собственность, и это перевод Linq следующим образом:

partial class Customer
{
    private static readonly CompiledExpression<Employee,IEnumerable<Contract>> activeContractsExpression
        = DefaultTranslationOf<Customer>
          .Property(c => c.ActiveContracts)
          .Is(c => c.Contracts.Where(x => x.ContractEndDate == null));

    public IEnumerable<Contract> ActiveContracts
    {
        get 
        { 
            // This is only called when you access your property outside a query
            return activeContractsExpression.Evaluate(this);
        }
    }
}

Тогда вы можете запросить это так:

var singleContractCustomers = db.Customers.WithTranslations()
                              .Where(w => w.ActiveContracts.Count() == 1);

Обратите внимание на звонок на WithTranslations(). Это создает специальную оболочку IQueryable, которая заменит все ссылки на ваше вычисляемое свойство его переводом до того, как выражение запроса будет передано Linq в SQL.

Кроме того, если вы включите пространство имен Microsoft.Linq.Translations.Auto вместо System.Linq, вам даже не нужно будет звонить WithTranslations()!

1 голос
/ 10 октября 2008

Это сработало как шарм. SQL-выражение, сгенерированное CustomersWithActiveContracts , выглядело отлично для меня.

{SELECT [t0].[CustomerID], [t0].[cFirstName], [t0].[cLastName]
FROM [dbo].[Customers] AS [t0]
WHERE ((
    SELECT COUNT(*)
     FROM [dbo].[Contracts] AS [t1]
    WHERE (([t1].[ContractEndDate] > @p0) OR ([t1].[ContractEndDate] IS NULL)) AND ([t1].[cId] = [t0].[cId])
    )) > @p1
}

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

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