Подтвердить что ты не робот

С# Динамический Linq/Queries

UPDATE2 Я начал играть больше с предложением Джеймса использовать рефлексию и получил что-то работающее, которое вернет значение свойства на основе строковой переменной. Я не хочу ставить это как ответ, хотя, как я считаю, это, вероятно, не лучшее решение. Вот код:

DataContext dataBase = new DataContext();
List<string> listOfFields = new List<string>;
List<string> listOfUsers = new List<string>


//strFields and strNames are strings generated from listOfFields and listOfUsers
IEnumerable<myUserData> userInfo = dataBase.ExecuteQuery<myUserData>
                       ("select " + strFields + " from myUserData where user_id                          in (" + strNames + ")");    

foreach(var user in userInfo)
{
    foreach(string field in listOfFields)
    {
        string fieldValue = user.GetType().GetProperty(field).GetValue(user,null).ToString
        Console.WriteLine(fieldValue);
    }

}

UPDATE: Я добиваюсь прогресса! Я думаю. Это может быть очень страшное решение и ресурсный боров, я еще не уверен, но это лучше, чем пустые экраны, которые я получал. Теперь проблема заключается в том, чтобы найти способ просто вернуть значения, а не имя поля с ним. Вот мой код:

foreach(string userID in listOfUserIDs)
{
    //dataBase is a Datacontext
    var userInfo = dataBase.myTable
                   .Where("user_id == @0", userID)
                   .Select("New(" + strFields + ")");

    foreach(var user in userInfo)
    {
         Console.WriteLine(user);
    }
}

Это выводит {first_name = JOHN, last_name = DOE} и т.д. любые другие поля. Я надеюсь, что смогу найти способ изменить предложение Select, чтобы просто выбрать значения.


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

Я успешно создал запрос, который будет получать правильную информацию, но я не понимаю, как получить доступ к определенным полям, которые были получены.

IEnumerable<myTable> userInfo = dataBase.ExecuteQuery<myTable>
("select " + Fields + " from myTable where userID in                 
(" + userNames + ")");

РЕДАКТИРОВАТЬ: Извините за мои плохие соглашения об именах, но myTable имеет тип: System.Collections.Generic.IEnumerable<DatabaseConnection.myTable>{System.Data.Linq.SqlClient.SqlProvider.OneTimeEnumerable<DatabaseConnection.myTable>} Это просто имя владельца места и имеет определенное имя в моей базе данных. Это было внутри папки Tables XD

myTable имеет тип класса DatabaseConnection. Поля - это строка полей, которые я просматриваю, а userNames - это строка из нескольких имен пользователей, каждая из которых разделена запятой. Примеры: Поля = "firstName, lastName, idNumber"         userNames = "Джон, Джейн, Боб"

Мой вопрос: возможно ли перебирать каждое из полей для каждого пользователя в типе объекта, который я сейчас возвращаю? Я видел, как это было сделано явно:

    foreach(var x in userinfo)
{
        Console.Writeline(x.firstName);
}

В моей текущей ситуации это не сработает, так как пользователю могут понадобиться другие поля, относящиеся к конкретным именам пользователей.

Я также пробовал использовать динамическую библиотеку Linq, и, хотя мне удалось успешно настроить мою строку выбора, я не уверен, как правильно перейти к настройке предложения where, когда userID будет равен нескольким различным строкам. (число имен не всегда будет одинаковым)

var dataInfo = dataBase.myTable
                       .Where("userID == @0", "(" + userIDs + ")")
                       .Select("New(" + fields + ")");

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

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

EDIT: Все это для Excel Addin, над которым я сейчас работаю. Мне нужно будет получить информацию в List или какой-либо форме объекта, который позволит мне помещать каждый результат поля в определенную ячейку.

EDIT: Извините, если я использую неправильную терминологию. Я чувствую, что моя ошибка может быть в моем перечислимом типе и называть ее "myTable" может быть запутанной. Это таблица, извлеченная из моего Server Explorer в моем .dbml файле в проекте.

РЕДАКТИРОВАТЬ: Насколько ужасно неэффективным/трудоемким является выполнение запроса для каждого отдельного пользователя и получение каждого запрошенного поля для этого имени пользователя? Я буду чувствовать себя глупо, если это то, что мне просто нужно, чтобы добавить простой цикл.

4b9b3361

Ответ 1

Если я правильно понимаю вашу проблему и вы хотите просто перебрать все свойства объекта (не зная, во что они находятся во время компиляции), вы можете использовать Reflection, например.

foreach (var prop in userInfo.GetType().GetProperties())
{
    Console.WriteLine("Property name: " + prop.Name);
    Console.WriteLine("Property value: " + prop.GetValue(userInfo, null));
}

Ответ 2

Так как вы используете LINQ to SQL, просто используйте LINQ для запроса данных. Я предполагаю, что вы создали модель DataContext и что MyTable - это элемент вашей модели, содержащий какую-то информацию о пользователе.

using (MyDataContext db = new MyDataContext()) {

    var results = (from m in db.MyTable
                   where m.UserId == userId
                   select m);

    // Loop over all of the rows returned by the previous query and do something with it
    foreach (var m in results) {
        // m represents a single row in the results, do something with it.
        Console.WriteLine(m.UserId);
    }

}

В качестве альтернативы, если вы ожидаете, что запрос вернет только одну строку, вы можете просто использовать Single или SingleOrDefault для проецирования результатов в один объект, а не в набор результатов:

using (MyDataContext db = new MyDataContext()) {

    var singleResult = (from m in db.MyTable
                        where m.UserId == userId
                        select m).SingleOrDefault();

    // Do something with the single result
    if (singleResult != null) {
        Console.WriteLine(singleResult.UserId);
    }

}

Edit:

В предыдущих примерах описываются основы того, как вы запрашиваете совместимый источник данных с помощью LINQ. Большинство провайдеров LINQ (таких как LINQ to SQL, как и вы) поддерживают интерфейс IQueryable. Краткое объяснение заключается в том, что этот интерфейс позволяет динамически создавать запросы и откладывает фактическое выполнение этого запроса до последней возможной минуты, когда вы на самом деле пытаетесь получить доступ к результатам. Объекты, возвращаемые запросом LINQ (например, переменная results в первом примере), возвращают IQueryable, где T - тип объекта, который вы запросили. Это не совсем коллекция в том же смысле, что вы можете подумать о List<T>, где элементы уже существуют или явно добавлены или удалены. В первом примере results фактически не имеет в нем никаких элементов, пока цикл foreach не попытается выполнить итерацию результатов.

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

Сначала давайте предположим, что ваша форма имеет три поля, которые вы хотите отфильтровать: Имя, Фамилия и Идентификатор пользователя. Чтобы это было просто, эти поля просто TextBoxes, и мы знаем, что они фильтруют поле, если в его соответствующем TextBox есть какой-либо вход. Предположим также, что проверка не требуется, и пользователь всегда вводит правильные данные в фильтр.

using (MyDataContext db = new MyDataContext()) {

    // Build out your base query, which would return everything from MyTable
    var results = (from m in db.MyTable
                   select m);

    // Check each textbox for a value to filter by.  Add the filter to the base
    // query where necessary.    

    if(!string.IsNullOrEmpty(txtUserId.Text)) {
        results = results.Where(x => x.UserId == (int) txtUserId.Text);
    }

    if(!string.IsNullOrEmpty(txtFirstName.Text)) {
        results = results.Where(x => x.FirstName == txtFirstName.Text);
    }

    if(!string.IsNullOrEmpty(txtLastName.Text)) {
        results = results.Where(x => x.LastName == txtLastName.Text);
    }

    // Loop over all of the rows returned by the previous query and do something with it.  
    // THIS is the actual point that the SQL would be generated from the query you've 
    // built and executed against the DB.  The elements returned by `results` 
    // should only meet all of the filters that you the user entered for one or
    // more of the fields.  Not entering any data would have resulted in all rows being
    // returned
    foreach (var m in results) {
        // m represents a single row in the results, do something with it.
        Console.WriteLine(m.UserId);
    }

}

Приведенный выше пример довольно прост, но он представляет собой типичный сценарий фильтрации, в котором вы хотели бы И И определенное количество полей вместе. Я уверен, что есть более элегантный способ инкапсулировать построение фильтра запросов, но основная концепция все равно будет такой же. Просто используйте интерфейс IQueryable<T> и привяжите к нему свои выражения фильтра.

Выполнение этого только позволяет объединять предикаты с использованием AND, и вы не можете сделать что-то вроде "Где у пользователя есть идентификатор 6 ИЛИ имеет имя" Джон ". Вы можете создавать более сложные выражения, подобные этим, используя пространство имен System.Linq.Expressions для построения выражений LINQ, но они относительно сложны и не требуются в большинстве сценариев.

Ответ 3

Хотя я не пробовал DataContext (или вообще .NET 4.5), в 4.0 вы можете использовать динамический класс для прямого доступа к полям:

IEnumerable<dynamic> userInfo = (from user in users
                                 where user.id equals userId
                                 select new { user.name, user.id /*, ...*/ }).ToList();

foreach (dynamic user in userInfo)
{
    Console.WriteLine(user.name);
    Console.WriteLine(user.id);
    //...
}

dynamic представляет класс, который разрешен во время выполнения, поэтому даже если при кодировании вы не можете видеть intellisense, показывая поля, если они верны, он будет работать.

Ответ 4

После обновления 2:

Возможно, вы можете упростить вложенный файл foreach в некоторый linq. Что-то вроде:

        PropertyInfo[] props = typeof(MyUserData).GetProperties();

        var results = from info in userInfo
                      from p in props
                      where listOfFields.Contains(p.Name)
                      select new
                      {
                          FieldName= p.Name,
                          UserId= info.UserId,
                          FieldValue= p.GetValue(info, null)
                      };

        foreach (var item in results)
        {
            Console.WriteLine("(userId = {0}) {1} = {2}", item.UserId, item.FieldName, item.FieldValue);
        }