получить данные для объекта c # - PullRequest
5 голосов
/ 04 января 2011

У меня есть класс Item, который представляет элемент в списке. В нем есть функция, которая вызывает хранимую процедуру, которая возвращает datatable, и мне нужно преобразовать datatable в Array элементов. Вот что я делаю:

public class Item
{
    private string _ItemIdDataName = "item_id";
    private string _ItemNameDataName = "item_name";
    private string _PriceDataName = "price";

    public long ItemId { get; set; }
    public string ItemName { get; set; }
    public float Price { get; set; }

    private Item(DataRow row)
    {
        if (row != null)
        {
            ItemId = long.Parse(row[_ItemIdDataName].ToString());
            ItemName = row[_ItemNameDataName].ToString();
            Price = float.Parse(row[_PriceDataName].ToString());
        }
    }

    public Item[] load()
    {
        DataTable dt=DBHandler.GetItems();//Stored procedure that returns DataTable 
        Item[] items = new Item[dt.Rows.Count];
        for (int i = 0; i < dt.Rows.Count; i++)
        {
            items[i] = new Item(dt.Rows[i]);
        }
        return items;
    }
}

Я правильно делаю? Как я могу улучшить это?

Ответы [ 4 ]

25 голосов
/ 04 января 2011

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

public static class DataTableExtensions
{
    public static IList<T> ToList<T>(this DataTable table) where T : new()
    {
        IList<PropertyInfo> properties = typeof(T).GetProperties().ToList();
        IList<T> result = new List<T>();

        foreach (var row in table.Rows)
        {
            var item = CreateItemFromRow<T>((DataRow)row, properties);
            result.Add(item);
        }

        return result;
    }

    public static IList<T> ToList<T>(this DataTable table, Dictionary<string, string> mappings) where T : new()
    {
        IList<PropertyInfo> properties = typeof(T).GetProperties().ToList();
        IList<T> result = new List<T>();

        foreach (var row in table.Rows)
        {
            var item = CreateItemFromRow<T>((DataRow)row, properties, mappings);
            result.Add(item);
        }

        return result;
    }

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
    {
        T item = new T();
        foreach (var property in properties)
        {
            property.SetValue(item, row[property.Name], null);
        }
        return item;
    }

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties, Dictionary<string, string> mappings) where T : new()
    {
        T item = new T();
        foreach (var property in properties)
        {
            if(mappings.ContainsKey(property.Name))
                property.SetValue(item, row[mappings[property.Name]], null);
        }
        return item;
    }
}

Теперь вы можетепросто позвоните

var items = dt.ToList<Item>();

или

var mappings = new Dictionary<string,string>();
mappings.Add("ItemId", "item_id");
mappings.Add("ItemName ", "item_name");
mappings.Add("Price ", "price);
var items = dt.ToList<Item>(mappings);

Сообщение в блоге здесь: http://blog.tomasjansson.com/2010/11/convert-datatable-to-generic-list-extension

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

Обновление: Создаемый вами объект (Item) должен иметь конструктор по умолчанию, в противном случаеприватный метод не сможет его создать.Поскольку решение работает в первую очередь для создания объекта, а не для получения значений объекта используйте свойства, полученные из отражения.

Обновление 2: Я добавил деталь ссловарь сопоставлений, но я не пробовал сам, поэтому он может не скомпилироваться.Тем не менее, концепция есть, и я думаю, что это работает.

5 голосов
/ 04 января 2011

Довольно хорошо, но у меня есть несколько предложений:

  • Не приводите вещи ToString для анализа к другому типу.Это может привести к повреждению вашего типа данных и является медленным / неэффективным.

  • Ожидайте и проверяйте нулевые значения от SQL Server.

Итак, вместо:

ItemId = long.Parse(row[_ItemIdDataName].ToString());

Попробуйте:

 ItemId = row.Field<long?>(_ItemIdDataName) ?? value_if_null; 

(Добавьте ссылку на System.Data.DatasetExtensions для получения расширения поля)

2 голосов
/ 23 марта 2013
0 голосов
/ 04 января 2011

Ваш частный конструктор Item и функции load() не кажутся принадлежащими в вашем Item классе.

Знаете, класс должен хорошо выполнять одну вещь и одну вещь.

Так что попробуйте
1. рефакторинг приватного c'tor, скажем, вспомогательного класса, который просто анализирует DataRow и возвращает экземпляр Item
2. и преобразовать load() в другой класс, который просто использует вышеупомянутый вспомогательный метод и возвращает массив экземпляров объекта Item

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