Лучший способ получить одно значение из DataTable? - PullRequest
3 голосов
/ 14 апреля 2010

У меня есть несколько статических классов, которые содержат такие таблицы:

using System;
using System.Data;
using System.Globalization;

public static class TableFoo
{
    private static readonly DataTable ItemTable;

    static TableFoo()
    {
        ItemTable = new DataTable("TableFoo") { Locale = CultureInfo.InvariantCulture };
        ItemTable.Columns.Add("Id", typeof(int));
        ItemTable.Columns["Id"].Unique = true;
        ItemTable.Columns.Add("Description", typeof(string));
        ItemTable.Columns.Add("Data1", typeof(int));
        ItemTable.Columns.Add("Data2", typeof(double));

        ItemTable.Rows.Add(0, "Item 1", 1, 1.0);
        ItemTable.Rows.Add(1, "Item 2", 1, 1.0);
        ItemTable.Rows.Add(2, "Item 3", 2, 0.75);
        ItemTable.Rows.Add(3, "Item 4", 4, 0.25);
        ItemTable.Rows.Add(4, "Item 5", 1, 1.0);
    }

    public static DataTable GetItemTable()
    {
        return ItemTable;
    }

    public static int Data1(int id)
    {
        DataRow[] dr = ItemTable.Select("Id = " + id);
        if (dr.Length == 0)
        {
            throw new ArgumentOutOfRangeException("id", "Out of range.");
        }

        return (int)dr[0]["Data1"];
    }

    public static double Data2(int id)
    {
        DataRow[] dr = ItemTable.Select("Id = " + id);
        if (dr.Length == 0)
        {
            throw new ArgumentOutOfRangeException("id", "Out of range.");
        }

        return (double)dr[0]["Data2"];
    }
}

Есть ли лучший способ написания методов Data1 или Data2, которые возвращают одно значение из одной строки, которое соответствует заданному идентификатору?

Обновление № 1:

Я создал метод расширения, который выглядит довольно неплохо:

public static T FirstValue<T>(this DataTable datatable, int id, string fieldName)
{
    try
    {
        return datatable.Rows.OfType<DataRow>().Where(row => (int)row["Id"] == id).Select(row => (T)row[fieldName]).First();
    }
    catch
    {
        throw new ArgumentOutOfRangeException("id", "Out of range.");
    }
}

Мой метод Data1 становится:

public static int Data1(int id)
{
    return ItemTable.FirstValue<int>(id, "Data1");
}

и Data2 становится:

public static double Data2(int id)
{
    return ItemTable.FirstValue<double>(id, "Data2");
}

Спасибо за все ваши ответы, но особенно Энтони Пегрэму, который дал замечательную единственную строку кода LINQ & Lambda.

Ответы [ 2 ]

3 голосов
/ 14 апреля 2010

Рассматривали ли вы использование Linq (для DataSets) ? При использовании выражений Linq эти функции Data1 и Data2 вообще не понадобятся, поскольку поиск и фильтрация могут выполняться в одной строке кода.

Пример добавления:

Съемка с бедра здесь, поэтому, пожалуйста, возьмите его с крошкой соли (не возле IDE:)

DataTable itemTbl = GetItemTable().AsEnumerable();

double dt1 = ((From t In itemTbl Where t.Id = <your_id> Select t).First())["Data1"];

Это две строки кода, но вы можете легко обернуть получение Enumerable.

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

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

int intValue = TableFoo.FirstValueOrDefault<int32>(7);
decimal decValue = TableFoo.FirstValueOrDefault<decimal>(7);

и если вам так хочется:

string strValue = TableFoo.FirstValueOrDefault<string>(7);
int? nintValue = TableFoo.FirstValueOrDefault<int?>(7);

Функция обрабатывает любой тип, который вы ей даете, строки, другие типы значений, обнуляемые типы, ссылочные типы. Если поле имеет значение null, функция возвращает значение «по умолчанию» для этого типа («» для строки). Если он абсолютно не может выполнить преобразование, поскольку вы запросили невозможное преобразование, оно выдаст ошибку. Я сделал это методом расширения для типа datarow (называемого ValueOrDefault), и эта присоска очень удобна .

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

Public Shared Function FirstValueOrDefault(Of T) (ByVal Int ID) As T
    Dim r as datarow = ItemTable.Select("Id = " + id.ToString());
    If r.IsNull(0) Then
        If GetType(T) Is GetType(String) Then
            Return CType(CType("", Object), T)
        Else
            Return Nothing
        End If
    Else
        Try
            Return r.Field(Of T)(0)
        Catch ex As Exception
            Return CType(r.Item(0), T)
        End Try
    End If
End Function
...