Извлечение предложения WHERE из LINQ to SQL - PullRequest
2 голосов
/ 30 ноября 2009

Я работаю с клиентом, который хочет смешать LINQ to SQL со своим собственным DAL. В конечном итоге они хотят иметь возможность запрашивать уровень, используя типичный синтаксис LINQ. Дело в том, что это усложняет то, что они строят свои запросы динамически. Поэтому в конечном итоге я хочу иметь возможность получать запрос LINQ, разбирать его на части и иметь возможность осматривать фрагменты, чтобы вытащить правильные объекты, но на самом деле я не хочу создавать фрагмент для перевода выражения «где» в SQL. Это то, что я могу просто сгенерировать с помощью кода Microsoft? Или есть более простой способ сделать это?

Ответы [ 6 ]

4 голосов
/ 30 ноября 2009

(вы имеете в виду просто LINQ, а не LINQ-to-SQL)

Конечно, вы можете это сделать - но это массивных объема работы. Вот как ; Я рекомендую "не". Вы также можете посмотреть исходный код DbLinq - посмотреть, как они это делают.

Если вы просто хотите Where, это будет немного проще - но как только вы начнете получать объединения, группировки и т. Д. - это будет очень трудно сделать.


Вот просто Where поддержка в пользовательской реализации LINQ ( не полностью запрашиваемый поставщик, но достаточно, чтобы получить LINQ с Where работающим):

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace YourLibrary
{
    public static class MyLinq
    {
        public static IEnumerable<T> Where<T>(
            this IMyDal<T> dal,
            Expression<Func<T, bool>> predicate)
        {
            BinaryExpression be = predicate.Body as BinaryExpression;
            var me = be.Left as MemberExpression;
            if(me == null) throw new InvalidOperationException("don't be silly");
            if(me.Expression != predicate.Parameters[0]) throw new InvalidOperationException("direct properties only, please!");
            string member = me.Member.Name;
            object value;
            switch (be.Right.NodeType)
            {
                case ExpressionType.Constant:
                    value = ((ConstantExpression)be.Right).Value;
                    break;
                case ExpressionType.MemberAccess:
                    var constMemberAccess = ((MemberExpression)be.Right);
                    var capture = ((ConstantExpression)constMemberAccess.Expression).Value;
                    switch (constMemberAccess.Member.MemberType)
                    {
                        case MemberTypes.Field:
                            value = ((FieldInfo)constMemberAccess.Member).GetValue(capture);
                            break;
                        case MemberTypes.Property:
                            value = ((PropertyInfo)constMemberAccess.Member).GetValue(capture, null);
                            break;
                        default:
                            throw new InvalidOperationException("simple captures only, please");
                    }
                    break;
                default:
                    throw new InvalidOperationException("more complexity");
            }
            return dal.Find(member, value);
        }
    }
    public interface IMyDal<T>
    {
        IEnumerable<T> Find(string member, object value);
    }
}
namespace MyCode
{
    using YourLibrary;
    static class Program
    {
        class Customer {
            public string Name { get; set; }
            public int Id { get; set; }

        }
        class CustomerDal : IMyDal<Customer>
        {
            public IEnumerable<Customer> Find(string member, object value)
            {
                Console.WriteLine("Your code here: " + member + " = " + value);
                return new Customer[0];
            }
        }
        static void Main()
        {
            var dal = new CustomerDal();
            var qry = from cust in dal
                      where cust.Name == "abc"
                      select cust;

            int id = int.Parse("123");
            var qry2 = from cust in dal
                      where cust.Id == id // capture
                      select cust;


        }
    }
}
1 голос
/ 30 ноября 2009

Технически, если ваш DAL предоставляет IQueryable<T> вместо IEnumerable<T>, вы также можете реализовать IQueryProvider и делать именно то, что вы описываете. Однако это не для слабонервных.

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

Я думаю, вы должны тщательно рассмотреть, что на самом деле необходимо от DAL, и разоблачить только это.

0 голосов
/ 02 июля 2015

Для кого-то еще с таким же вопросом. Извлечь предложение where из LINQ-to-SQL не так просто, как можно было бы надеяться. Кроме того, делать это само по себе, вероятно, бессмысленно. Есть несколько опций, в зависимости от требований - либо извлечь ее из сгенерированной строки, но тогда она будет содержать ссылки на параметры и сопоставления свойств объекта, которые также должны быть разрешены, поэтому их также нужно будет извлечь из Оригинальный провайдер как-то, иначе это было бы бессмысленно. Другой вариант - найти модульного провайдера, который может это сделать, а также сделать сопоставления элементов легко доступными, но, опять же, без остальной части запроса, я вижу небольшую полезность в этом, потому что предложение where будет ссылаться на таблицу / псевдонимы столбцов из оператора select.

У меня была похожая задача написать пару лет назад полноценного провайдера для пользовательского ORM / DAL. Хотя это квалифицируется как самая сложная вещь, над которой я работал, будучи опытным разработчиком, я могу сказать, что это не так плохо, как утверждают некоторые люди, как только вы начинаете думать о концепциях, лежащих в основе такого компонента. Некоторые решения, которые я видел, идут не так, как надо, добавляют избыточную функциональность и имеют дополнительный код для решения проблем, связанных с базовой логикой. Например. этап / модуль «оптимизации», который пытается повторно разложить раздутый, вложенный SQL, созданный основным анализатором. Если последний был спроектирован таким образом, чтобы с самого начала выводить чистый SQL, то фаза очистки не потребовалась бы. Я видел поставщиков, которые создают новый уровень вложенности для каждого запроса where и join. Это плохая стратегия. Разбивая запрос на три / четыре основных части - выберите, откуда и где - по порядку, которые строятся отдельно при посещении дерева, эта проблема полностью исключается. Я разработал объект-к-данным (он же LINQ-to-SQL), предоставленный на основе этих принципов для пользовательского ORM / DAL, и он производит хороший, чистый SQL с превосходной производительностью, так как каждый оператор компилируется в IL и кэшируются.

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

0 голосов
/ 30 ноября 2009
0 голосов
/ 30 ноября 2009

Только некоторые, хотя. Я знаю некоторую языковую поддержку построения строки, которая может быть выполнена в самом коде. Я никогда не пробовал с .Net, но это распространено в функциональных языках, таких как LISP. Так как .Net поддерживает лямбды, возможно, это возможно. Поскольку F # скоро появится в .Net, возможно, это будет возможно, если не сейчас.

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

0 голосов
/ 30 ноября 2009

Я только что прочитал интересную статью о деревьях выражений, LINQ to SQL использует их для перевода запроса в SQL и отправки его по сети.

Может быть, это то, что вы могли бы использовать?

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