Можно ли объединить несколько скомпилированных запросов linq? - PullRequest
3 голосов
/ 09 октября 2009

Я пытаюсь объединить несколько скомпилированных запросов linq. Мне удалось объединить два запроса в цепочку, но я не могу заставить цепочку из трех работать правильно. Итак, вот сокращение моего кода, чтобы воссоздать проблему. Мои два вопроса: «Почему это не работает?» и «Есть ли лучший способ сохранить выигрыш в производительности скомпилированных запросов, а также избежать дублирования базовой логики запросов, которая обычно используется?»

Определите следующие два запроса:

Func<DataContext, IQueryable<User>> selectUsers = 
    CompiledQuery.Compile(
        (DataContext dc)=>dc.Users.Select(x=>x)
    );
//        
Func<DataContext, string, IQueryable<User>> filterUserName = 
    CompiledQuery.Compile(
        (DataContext dc, string name) =>
            selectUsers(dc).Where(user=>user.Name == name)
    );

Вызов и перечисление цепочки работает нормально:

filterUserName(new DataContext(), "Otter").ToList();

Добавить третий запрос в цепочку:

Func<DataContext, string, int, IQueryable<User>> filterUserAndGroup =     
    CompiledQuery.Compile(
        (DataContext dc, string name, int groupId) => 
            filterUserName(dc, name).Where(user=>user.GroupId == groupId)
    );

Вызов по цепочке не работает:

filterUserAndGroup(new DataContext(), "Otter", 101);

System.InvalidOperationException: Доступ участника «Строковое имя» пользователя не законно по типу «System.Linq.IQueryable 1[User].. at System.Data.Linq.SqlClient.SqlMember.set_Expression(SqlExpression value) at System.Data.Linq.SqlClient.SqlFactory.Member(SqlExpression expr, MemberInfo member) at System.Data.Linq.SqlClient.SqlBinder.Visitor.AccessMember(SqlMember m, SqlExpression expo) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitMember(SqlMember m) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitBinaryOperator(SqlBinary bo) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias a) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource source) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitIncludeScope(SqlIncludeScope scope) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Bind(SqlNode node) at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection 1 parentParameters, SqlNodeAnnotations аннотации) в System.Data.Linq.SqlClient.SqlProvider.BuildQuery (Expression запрос, SqlNodeAnnotations аннотации) в System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Compile (Expression запрос) в System.Data.Linq.CompiledQuery.ExecuteQuery (DataContext context, Object [] args) в System.Data.Linq.CompiledQuery.Invoke (TArg0 arg0, TArg1 arg1) в TestMethod () в ....

Ответы [ 3 ]

3 голосов
/ 09 октября 2009

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

С MSDN CompiledQuery :

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

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

Возможно, этот код исправит это, хотя это может повлиять на возврат обратно в базу данных, если вы используете LINQ to SQL.

filterUserName(dc, name).ToList().Where(user=>user.GroupId == groupId)
0 голосов
/ 09 октября 2009

Вам нужно использовать класс CompiledQuery? Попробуйте это ...

static Func<DataContext, IQueryable<User>> selectUsers =
  (dc) => dc.Users.Select(x => x);
//        
static Func<DataContext, string, IQueryable<User>> filterUserName =
    (DataContext dc, string name) =>
        selectUsers(dc).Where(user => user.Name == name);
//
static Func<DataContext, string, int, IQueryable<User>> filterUserAndGroup =
    (DataContext dc, string name, int groupId) =>
        filterUserName(dc, name).Where(u => u.GroupID == groupId);

... тестовый код (я знаю, что мой DataContext здесь не LINQ2SQL, но в этом и состоит удовольствие и красота LINQ) ...

Кроме того, я использую этот метод в отношении своих собственных баз данных, поэтому я знаю, что они встроены в отдельные запросы для отправки в базу данных. Я даже использовал обычные методы экземпляров, которые возвращают IQueryable <> вместо Func <> делегатов.

public class DataContext
{
    public static Func<DataContext, IQueryable<User>> selectUsers =
      (dc) => dc.Users.Select(x => x);
    //        
    public static Func<DataContext, string, IQueryable<User>> filterUserName =
        (DataContext dc, string name) =>
            selectUsers(dc).Where(user => user.Name == name);
    //
    public static Func<DataContext, string, int, IQueryable<User>> UsrAndGrp =
        (DataContext dc, string name, int groupId) =>
            filterUserName(dc, name).Where(u => u.GroupID == groupId);

    public DataContext()
    {
        Users = new List<User>()
        {
            new User(){ Name = "Matt", GroupID = 1},
            new User(){ Name = "Matt", GroupID = 2},
            new User(){ Name = "Jim", GroupID = 2},
            new User(){ Name = "Greg", GroupID = 2}
        }.AsQueryable();
    }
    public IQueryable<User> Users { get; set; }
    public class User
    {
        public string Name { get; set; }
        public int GroupID { get; set; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var q1 = DataContext.UsrAndGrp(new DataContext(), "Matt", 1);
        Console.WriteLine(q1.Count()); // 1
        var q2 = DataContext.filterUserName(new DataContext(), "Matt");
        Console.WriteLine(q2.Count()); // 2
    }
}
0 голосов
/ 09 октября 2009

Правда, я не знаком с CompiledQuery. Но из-за природы отложенного выполнения LINQ вы можете сделать что-то вроде этого:

var result = dbContext.Users.Where(user => user.id == id);
result = result.Where(user => user.GroupID == groupID);
result = result.Select(user => user.username);

for(String username in result){ 
   ; // do something
}

Выше приведен простой пример. Но это может быть весьма полезно, когда, скажем, объединяются различные запросы на основе ввода пользователя (например, формы «расширенного поиска» на веб-сайте).

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