Свой класс для работы с DataTable

343
28 февраля 2017, 14:38

Всем привет. Появилась необходимость работать с данными, которые нужно будет сохранять на жёсткий диск. Решил использовать DataTable и хранить их в виде xml файлов. Таблиц в проге планируется несколько. Решил оформить в отдельный класс, код которого будет ниже. Есть некие общие методы, дублирующиеся для каждой таблицы. Есть для каждой таблицы свои методы. Вот код:

public class Tables
{     
    public static class Groups
    {
        // возвращаем всю строку по айди
        public static DataRow GetFullRow(int id)
        {
            return dt.Select("id='" + id.ToString() + "'")[0];
        }
        public static DataRow GetRow(string query)
        {
            return dt.Select(query)[0];
        }
        static string xml_path;
        public static DataTable dt;
        public static void ReadXml()
        {
            xml_path = Main.GetXmlPath(dt.TableName);
            if (File.Exists(xml_path))
            {
                dt.ReadXml(xml_path);
            }
        }
        public static void SaveToXml()
        {
            dt.WriteXml(xml_path);
        }
        public static void Create()
        {
            dt = new DataTable("Groups");
            dt.Columns.Add("id", typeof(int));
            dt.Columns.Add("name", typeof(string));
            dt.PrimaryKey = new DataColumn[] { dt.Columns[0] };
            dt.Columns[0].AutoIncrement = true;
            dt.Columns[0].AutoIncrementStep = 1;
            ReadXml();
        }
        public static void Add(bool save_after, DataRow dr)
        {
            dt.Rows.Add(dr);
            if (save_after)
                SaveToXml();
        }
    }
    public static class Items
    {
        public static void SaveToXml()
        {
            dt.WriteXml(xml_path);
        }
        static string xml_path;
        public static DataTable dt;
        public static void ReadXml()
        {
            xml_path = Main.GetXmlPath(dt.TableName);
            if (File.Exists(xml_path))
            {
                dt.ReadXml(xml_path);
            }
        }
        public static void Create()
        {
            dt = new DataTable("Items");
            dt.Columns.Add("id", typeof(int));
            dt.Columns.Add("name", typeof(string));
            dt.Columns.Add("cost", typeof(double));
            dt.Columns.Add("id_group", typeof(int));
            dt.Columns.Add("is_periodic", typeof(bool));
            dt.Columns.Add("date", typeof(string));
            dt.Columns.Add("period", typeof(int));
            dt.Columns.Add("is_dynamic", typeof(bool));
            dt.Columns.Add("calc_type", typeof(int));
            dt.Columns.Add("days", typeof(int));
            dt.Columns.Add("val_to_multiply", typeof(double));
            dt.Columns.Add("payday", typeof(DateTime));
            dt.PrimaryKey = new DataColumn[] { dt.Columns[0] };               
            dt.Columns[0].AutoIncrement = true;
            dt.Columns[0].AutoIncrementStep = 1;
            ReadXml();
        }
        // метод добавляет строку в таблицу
        // а если такой id уже есть, то изменяет
        public static void Add(bool save_after, DataRow dr)
        {
            dt.Rows.Add(dr);
            if (save_after)
                SaveToXml();
        }
        public static void EditRow(bool save_after,DataRow dr,int id)
        {               
            DataRow found = dt.Select("id='" + id.ToString() + "'")[0];
            int cnt = found.ItemArray.Count();
            for (int i = 1; i < cnt; i++)
            {
                try
                {
                    found[i] = dr[i];
                }
                catch { continue; }
            }
            if (save_after)
                SaveToXml();
        }
        // возвращаем всю строку по айди
        public static DataRow GetFullRow(int id)
        {
            return dt.Select("id='"+id.ToString() + "'")[0];
        }
        public static DataRow GetRow(string query)
        {
            return dt.Select(query)[0];
        }
        public static void RemoveById(bool save_after,int id)
        {
            dt.Rows.Remove(dt.Select("id='" + id.ToString() + "'")[0]);
            if (save_after)
                SaveToXml();
        }

    public static class JobMoney
    {
        public static void SaveToXml()
        {
            dt.WriteXml(xml_path);
        }
        static string xml_path;
        public static DataTable dt;
        public static void ReadXml()
        {
            xml_path = Main.GetXmlPath(dt.TableName);
            if (File.Exists(xml_path))
            {
                dt.ReadXml(xml_path);
            }
        }
        public static void Create()
        {
            dt = new DataTable("JobMoney");
            dt.Columns.Add("id", typeof(int));
            dt.Columns.Add("date", typeof(int)); // число месяца
            dt.Columns.Add("size", typeof(double));
            dt.PrimaryKey = new DataColumn[] { dt.Columns[0] };
            dt.Columns[0].AutoIncrement = true;
            dt.Columns[0].AutoIncrementStep = 1;
            ReadXml();
        }
        // метод добавляет строку в таблицу
        // а если такой id уже есть, то изменяет
        public static void Add(bool save_after, DataRow dr)
        {
            dt.Rows.Add(dr);
            if (save_after)
                SaveToXml();
        }
        // возвращаем всю строку по айди
        public static DataRow GetFullRow(int id)
        {
            return dt.Select("id='" + id.ToString() + "'")[0];
        }
    }
    public static class Main
    {
        public static void InitAll()
        {
            Groups.Create();
            Items.Create();
            JobMoney.Create();
        }
        public static string GetXmlPath(string tableName)
        {
            return String.Format(
                "{0}\\{1}.xml", Application.StartupPath,tableName
                );
        }
    }
}

Вопрос в следующем - насколько правильно оформлять работу с таблицами именно таким образом. Каким образом сделать так, чтобы не дублировать одинаковый код для разных таблиц.

сделал так:

 public abstract class test
    {
        protected string Xml_path;
        protected DataTable dt { get; set; }
        protected test(string tablename)
        {
            dt = new DataTable();
            dt.Columns.Add("id",typeof(int));
            dt.Columns.Add("name",typeof(string));
            dt.PrimaryKey = new DataColumn[] { dt.Columns[0] };
            dt.Columns[0].AutoIncrement = true;
            dt.Columns[0].AutoIncrementStep = 1;
            dt.TableName = tablename;
            Xml_path = Application.StartupPath + "\\" + tablename + ".xml";
        }
        protected void SavetoXml()
        {
            dt.WriteXml(Xml_path);
        }
        protected void ReadXml()
        {
            if (File.Exists(Xml_path))
            {
                try
                {
                    dt.ReadXml(Xml_path);
                }
                catch (Exception ex)
                {
                    string x = ex.Message;
                }
            }
        }
    }
    public class t1 : test
    {
      public t1(string tablename) : base(tablename)
        {
            // добавляем недостающие поля в таблицу
            dt.Columns.Add("test");
            //  грузим данные из xml 
            ReadXml();
        }
        public void t1_method()
        {
            MessageBox.Show("URA!!!");
        }
    }
    public class t2 : test
    {
        public t2(string tablename) : base(tablename)
        {
        }
    }
    public class t3 : test
    {
        public t3(string tablename) : base(tablename)
        {
        }
    }
Answer 1

Сделай общий класс, а от него уже наследуй отдельные классы для каждой таблицы, и тебе не придётся дублировать код.

Answer 2

Наследование

В вашем коде дублирование убирается приминением паттерна «шаблонный метод».

Решением вашей проблемы является создание базового класса Table с последующей специализацией наследников до GroupsTable, ItemsTable, JobMoneyTable. Все общие части алгоритма следует перенести в Table, а те части алгоритма, которые изменяются в зависимости от таблицы — в наследника.

Небольшая статья о наследовании на MSDN

API

У ваших классов (Groups, Items, JobMoney) есть одна общая и очень серьезная проблема. В них нарушается инвариант в зависимости от очередности вызова методов.

Например, что будет, если я напишу следующий код:

Groups.SaveToXml();

До вызова метода Create()? Ответ — NullReferenceException так как поле db ещё не инициализировано. Нужно стараться избегать такого проектирования, при котором корректность инвариантов класса зависит от очередности вызова его методов.

READ ALSO
Регистрация на сайте с помощью ASP.NET MVC C#

Регистрация на сайте с помощью ASP.NET MVC C#

Делаю регистрацию на сайте в учебных целяхХочу запретить создавать пользователей с одинаковым логином

417
Вывод таблицы результатов мини-игры в текстовый файл

Вывод таблицы результатов мини-игры в текстовый файл

Здравствуйте! Такая проблемаХочу вывести таблицу результатов примитивного вида: Ниже мой недописанный код

372
Спрайты, Тайтлы и их разрешение в Unity 3d

Спрайты, Тайтлы и их разрешение в Unity 3d

Приветствую! Клепаю 2D игрушку на небезызвестном двигателе Unity3DВозник вопрос сразу же после отрисовки первого уровня

327
Как удалить используемый файл

Как удалить используемый файл

Я пишу на C# программу, которая с помощью библиотеки log4net ведет лог в отдельный файлКогда лог-файл переполняется, он должен быть удален

237