LinqToSql Выберите класс, затем выполните больше запросов - PullRequest
3 голосов
/ 23 апреля 2010

У меня есть запрос LINQ с несколькими объединениями, и я хочу передать его как IQueryable<T> и применить дополнительные фильтры в других методах.

Проблема в том, что я не могу понять, как передать тип данных var и держать его строго типизированным, и если я пытаюсь поместить его в свой собственный класс (EG: .Select((a,b) => new MyClass(a,b))), я получаю ошибки когда я пытаюсь добавить более поздние Where предложения, потому что мой класс не имеет переводов на SQL. Есть ли способ, которым я могу сделать одно из следующих действий:

  1. Сделать мою карту классов в SQL?
  2. Заставить тип данных var реализовать интерфейс (чтобы я мог передать его, как будто это так)?
  3. Что-то, чего у меня нет, решит мою проблему?

Пример:

public void Main()
{
    using (DBDataContext context = new DBDataContext())
    {
      var result = context.TableAs.Join(
         context.TableBs,
         a => a.BID,
         b => b.ID,
        (a,b) => new {A = a, B = b}
      );
      result = addNeedValue(result, 4);
   }
}

private ???? addNeedValue(???? result, int value)
{
    return result.Where(r => r.A.Value == value);
}

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

Ответы [ 4 ]

2 голосов
/ 23 апреля 2010

Все, что вам нужно сделать, это деанонимизировать ваш тип, который вы создаете в результате запроса - new { A = a, B = b } Просто создайте класс с этими свойствами и назовите его соответствующим образом.Тогда ваш запрос будет иметь тип IQueryable<your type>

Примерно так:

public class MyClass 
{ 
    public int A { get; set; }
    public int B { get; set; }
}

public void Main()
{
    using (DBDataContext context = new DBDataContext())
    {
      var result = context.TableAs.Join(
         context.TableBs,
         a => a.BID,
         b => b.ID,
        (a,b) => new MyClass {A = a, B = b}
      );
      result = addNeedValue(result, 4);
   }
}

private IQueryable<MyClass> addNeedValue(IQueryable<MyClass> result, int value)
{
    return result.Where(r => r.A.Value == value);
}
1 голос
/ 26 апреля 2010

Вот два разных подхода;первый применяет фильтр перед , выполняющим Join, поскольку объединенные запросы не должны быть просто базовыми таблицами.Второй подход применяет фильтр после объединения, используя промежуточную проекцию (и изменяя его на возвращать предикат, а не применяя его внутренне).был успешно протестирован на .NET 3.5 и .NET 4;обратите внимание, что в 3.5 (SP1) Expression.Invoke (для второго примера) не будет работать на EF, но подходит для LINQ-to-SQL.

Если вы хотите запустить пример, я 'мы использовали Northwind (только потому, что это то, что у меня было локально):

using System;
using System.Linq;
using System.Linq.Expressions;
using ConsoleApplication1; // my data-context's namespace
static class Program
{
    public static void Main()
    {
        using (var context = new TestDataContext())
        {
            context.Log = Console.Out; // to check it has worked
            IQueryable<Order> lhs = context.Orders;
            IQueryable<Order_Detail> rhs = context.Order_Details;
            // how ever many predicates etc here
            rhs = addBeforeJoin(rhs, 4);

            var result = lhs.Join(rhs,
                   a => a.OrderID,
                   b => b.OrderID,
                  (a, b) => new { A = a, B = b }
            );
            // or add after
            result = result.Where(row => row.B, addAfterJoin(100));
            Console.WriteLine(result.Count());
        }
    }

    private static IQueryable<Order_Detail> addBeforeJoin(IQueryable<Order_Detail> query, int value)
    {
        return query.Where(r => r.Quantity >= value);
    }
    private static Expression<Func<Order_Detail, bool>> addAfterJoin(int value)
    {
        return r => r.Quantity <= value;
    }
    private static IQueryable<TSource> Where<TSource, TProjection>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, TProjection>> selector,
        Expression<Func<TProjection, bool>> predicate)
    {
        return source.Where(
            Expression.Lambda<Func<TSource, bool>>(
            Expression.Invoke(predicate, selector.Body),
            selector.Parameters));
    }

}
0 голосов
/ 23 апреля 2010

Я определенно не рекомендую его, но вы, вероятно, можете использовать динамический здесь, если вы используете .NET 4.0.

0 голосов
/ 23 апреля 2010

Вы возвращаете то, что известно как «анонимный тип» из вашего объединения - это не конкретный тип базы данных, а просто объект, содержащий A и B. Поскольку тип анонимный, я не думаю, что выЯ собираюсь написать функцию, которая возвращает этот тип - или коллекцию этого типа.

Если бы это был тип, о котором SQL / LINQ-to-SQL знал, то вы, вероятно, могли бы сделатьthis.

Одним из способов заставить LINQ-to-SQL распознавать тип может быть создание хранимой процедуры, которая выполняет соединение и выбирает соответствующие столбцы.Если вы добавите эту хранимую процедуру в файл DBML, LINQ-to-SQL поймет тип каждой «строки» в результате.После этого ваша функция сможет вернуть IQueryable<ResultSetRow>.

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