Построить запрос с помощью Linq, а не строк SQL - PullRequest
0 голосов
/ 22 сентября 2010

У меня есть серверный элемент управления 2.0, который использует динамический запрос примерно в форме:

string sql = "Select " + columnvariable + " FROM " + tablenamevariable

Итак, очевидно, вы можете дать ему любое допустимое имя столбца из любой допустимой таблицы в БД ион возвращает значения в столбце в DataReader (в данном случае).

Я пытаюсь сократить количество явного фрагментарного SQL в кодовой базе и предпочел бы сделать это в LINQ.Есть простой способ сделать это?Это даже желательно?Я полагаю, что получающийся в результате частичный SQL в этом случае будет настолько общим, что в этом случае не создаст проблемы безопасности.

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

Я не имею в виду этобыть дискуссией.Мне бы хотелось получить ответ типа «Да, это возможно и тривиально, вот как ...» или «Да, но только если вы создадите этот сложный набор классов-обработчиков и в основном переписаете части LINQ, вот как ...»

Мне, однако, было бы интересно узнать, могут ли люди думать, что подобные вещи в LINQ лучше всего можно охарактеризовать как a) очень хорошую идею или b) сумасшедший разговор.

Ответы [ 7 ]

1 голос
/ 22 сентября 2010

Поскольку вы искали простой ответ ... Нет, это невозможно в LINQ.

LINQ строго типизирован, в то время как вы ищете что-то совершенно динамичное.Если ваша таблица и столбец являются переменными времени выполнения (т. Е. Строками), тогда ваш SQL должен быть строкой ... или вам нужно будет использовать ORM (NHibernate, L2S, EF и т. Д.) Для доступа к данным.

0 голосов
/ 22 сентября 2010

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

public IQueryable<TResult> GetColumn<TResult>(string tableName, string columnName)
{
    // Get the table, e.g. for "Customers" get db.Customers
    var table = db.GetType().GetField(tableName).GetValue(db);

    // Find the specific type parameter (the T in IQueryable<T>)
    var iqueryableT = table.GetType().FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
    var type = iqueryableT.GetGenericArguments()[0];

    // Construct a query that retrieves the relevant column
    var param = Expression.Parameter(type);
    var prop = Expression.Property(param, columnName);
    var expression = Expression.Lambda(prop, param);

    // Call Queryable.Select() with the appropriate parameters
    // Notice there are two Select methods, need to get the right one...
    return (IQueryable<TResult>) typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Where(meth => meth.Name == "Select")
        .Select(meth => meth.MakeGenericMethod(type, typeof(TResult)))
        .Where(meth => meth.GetParameters()[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(type, typeof(TResult))))
        .First()
        .Invoke(null, new object[] { table, expression });
}

И тогда вы можете назвать это так:

var customerNames = GetColumn<string>("Customers", "CustomerName");

Конечно, вам все равно нужно знать тип столбца, потому что вы хотите, чтобы он возвращалсяправильно строго типизированный запрос.В качестве альтернативы вы можете добавить Expression.Convert, если хотите, чтобы он автоматически преобразовывал все данные в строки;тогда вам не нужно каждый раз указывать string, но, конечно, данные вашего столбца будут потенциально преобразованы в строки с потерями.

0 голосов
/ 22 сентября 2010

К сожалению, ваш вопрос не проясняет, откуда именно columnvariable, но если мне позволят сделать следующие предположения, то я думаю, что могу предоставить вам почти тривиальное решение:

  • Предположение 1: columnvariable - это значение, которое передается через вызовы методов, но в конечном итоге это константа.Т.е. это не предоставленная пользователем строка, она не читается из файла и т. Д.

  • Предположение 2: У вас есть доступ ко всему коду, который передается в этих константах для columnvariable.

Если эти предположения верны, то я рекомендую изменить ваш метод, содержащий это:

string sql = "Select " + columnvariable + " FROM " + tablenamevariable

, на что-то более похожее на это:

public IQueryable<Customer> GetCustomers<T>(Expression<Func<Customer, T>> column)
{
    return db.Customers.Select(column);
}

, а затем вместо

// Assuming "CustomerName" is the name of a column in the Customers table
var myCustomers = GetCustomers("CustomerName");

вы бы сказали

// Properly type-safe and compile-time checked
var myCustomers = GetCustomers(c => c.CustomerName);
0 голосов
/ 22 сентября 2010

Вы идете об этом под неправильным углом.Вы думаете, что хотите это:

void DoStuff(tablenamevariable, columnvariable)
{
    string sql = "Select " + columnvariable + " FROM " + tablenamevariable 
    // read data 
}


DoStuff("MyTable", "MyColumn");

Почему этот интерфейс?Что не так с этим:

void DoStuff<T>(IQueryable<T> query)
{
      // read data
}


DoStuff(from t in MyTable select t.MyColumn);

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

DoStuff(from t in MyTable where t.OtherColumn == 5 select t.MyColumn);
0 голосов
/ 22 сентября 2010

Ваш запрос:

string sql = "Select " + columnvariable + " FROM " + tablenamevariable

можно «перевести» с помощью LINQ следующим образом:

var sql = (from tablenamevariable select columnvariable).ToList();

и теперь 'Переменная sql содержит список переменных столбца, хранящихся в таблице переменных.

С LINQ вы можете сделать это намного проще.

0 голосов
/ 22 сентября 2010

Вот пример из доброго старого Скотта Гу (thrie):

Dynamic LINQ, часть 1

0 голосов
/ 22 сентября 2010

Эта запись в блоге Рика Строля, кажется, говорит о каком-то смысле этой темы:

http://www.west -wind.com / Weblog / сообщений / 314663.aspx

В отсутствие чего-либо еще более ясного, я думаю, я сделаю свой выбор на основе этого.

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

РЕДАКТИРОВАТЬ: Хотя это связано с вышеупомянутой статьей это:

http://www.west -wind.com / WebLog / ShowPost.aspx? ID = 134706

прекрасно проясняет мою проблему и фактически является своего рода приквелом к ​​другой статье. Увлекательные вещи.

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