Параметризованные формы
От: Аноним  
Дата: 17.11.10 06:34
Оценка:
Доброго времени суток!

При разработке столкнулись со следующей проблемой:
есть достаточно большое количество бизнес-сущностей, которые хранятся в базе данных.
При необходимости работы с ними — бизнес-объекты загружаются из базы данных.

Все бизнес-объекты (для удобства) реализуют простейший интерфейс:


    public interface IEntity
    {
        /// <summary>
        /// Уникальный номер сущности в базе данных
        /// </summary>
        int ID { get; set; }
        
        /// <summary>
        /// Осуществляет сохранение объекта
        /// </summary>
        void Save();
    }


Этот интерфейс реализуется следующим образом


    public class Customer : IEntity
    {

        public delegate void UpdatedEventHandler(Customer sender, EventArgs e);
        public event UpdatedEventHandler Updated; 

        #region Служебные переменные
        /// <summary>
        /// Уникальный код клиента
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// Инн клиента
        /// </summary>
        public string IdentificationNumber { get; set; }

        /// <summary>
        /// Адрес регистрации (прописки) клиента
        /// </summary>
        public Address RegistrationAddress { get; set; }

        /// <summary>
        /// Адрес проживания (фактический) адрес клиента
        /// </summary>
        public Address ResidenceAddress { get; set; }

        /// <summary>
        /// Вид деятельности (клиента)
        /// </summary>
        public ActivityType ActivityType { get; set; }

        /// <summary>
        /// Первый контактный телефон клиента
        /// </summary>
        public string ContactPhone1 { get; set; }

        /// <summary>
        /// Второй контактный телефон клиента
        /// </summary>
        public string ContactPhone2 { get; set; }

        /// <summary>
        /// Электронный адрес клиента
        /// </summary>
        public string EMail { get; set; }
 ...

        #region public void Save()
        /// <summary>
        /// Осуществляет сохранение информации о клиенте в базе данных
        /// </summary>
        public void Save()
        {
            this.daCustomer.Save(this);
            
            if (Updated != null)
                Updated(this, EventArgs.Empty);
        } 
        #endregion
}


Теперь необходимо вытащить объект на форму для пользовательского редактирования.
Алгоритм работы с формой стандартный:

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

— провести валидацию данных
— "собрать" объект на основе тех данных, которые внес пользователь
— осуществить сохранение данных

Класс-формы выглядит следующим образом


using System;
using System.Windows.Forms;

using Entities.Management;

namespace Entities.Forms
{
    /// <summary>
    /// Базовая форма для отображения и редактирования объектов
    /// </summary>
    /// <typeparam name="T">Тип объекта для отображения</typeparam>
    public partial class fParent <T>: Form
        where T : IEntity
    {
        /// <summary>
        /// Объект, для которого осуществляется редактирование и просмотр свойств
        /// </summary>
        protected T Item { get; set; }

        /// <summary>
        /// Создание новой формы - справочника
        /// </summary>
        public fParent()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Осуществляет загрузку данных с формы в объект
        /// </summary>
        protected virtual void ComposeData()
        {
        }

        /// <summary>
        /// Осуществляет заполнение элементов формы
        /// </summary>
        protected virtual void FillElements()
        {
        }

        /// <summary>
        /// Осуществляет валидацию данных на форме
        /// </summary>
        /// <param name="message">Сообщение, которое выдается пользователю в случае ошибки</param>
        /// <returns>True, если валидация прошла успешно. False - в противном случае</returns>
        protected virtual bool ValidateData(out string message)
        {
            message = "";
            return true;
        }

        /// <summary>
        /// Осуществляет сохранение данных с формы
        /// </summary>
        protected virtual void SaveData()
        {
            Item.Save();
        }

        /// <summary>
        /// Осуществляет применение настроек безопасности
        /// </summary>
        protected virtual void ApplySecuritySettings()
        {
        }

        /// <summary>
        /// Осуществляет редактирование данных объекта
        /// </summary>
        /// <param name="item">Описание объекта для редактирования</param>
        /// <param name="showModal">Следует ли отображать окно в диалоговом режиме</param>
        /// <returns></returns>
        public DialogResult EditItem(T item, bool showModal)
        {
            this.Item = item;
            this.FillElements();
            this.ApplySecuritySettings();

            if (showModal)
                return this.ShowDialog();
            else
            {
                this.MdiParent = UIManager.MDIForm;
                this.Show();
                return DialogResult.OK;
            }
        }


        #region private void bSave_Click(object sender, EventArgs e)
        private void bSave_Click(object sender, EventArgs e)
        {
            // осуществляет сохранение данных об объекте

            string message;
            bool ok = ValidateData(out message);

            if (!ok)
            {
                MessageBox.Show("Во время верификации данных произошла ошибка:" + Environment.NewLine +
                    Environment.NewLine + message, Settings.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            try
            {
                this.ComposeData();
                this.SaveData();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Во время сохранения данных произошла ошибка:" + Environment.NewLine +
                    Environment.NewLine + ex.Message, Settings.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            this.DialogResult = DialogResult.OK;
            this.Close();
        } 
        #endregion

        #region private void bCancel_Click(object sender, EventArgs e)
        private void bCancel_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        } 
        #endregion

    }
}


Дизайнер форм прекрасно понимает и работает с этой формой.
Теперь, когда дело доходит до отдельной формы, которая представляет отдельный бизнес-объект. Имеем следующее:


    /// <summary>
    /// Отображение формы для редактирования отдельной доверенности
    /// </summary>
    public partial class fCustomer : fParent <Customer>
    {

        #region public fCustomer()
        /// <summary>
        /// Создание новой формы для отдельного клиента
        /// </summary>
        public fCustomer()
            : base()
        {
            InitializeComponent();
        } 
        #endregion


        #region protected override void FillElements()
        /// <summary>
        /// Осуществляет заполнение данных
        /// </summary>
        protected override void FillElements()
        {
            string accountName = Item.Account.AccountName;
            this.tAccountName.Text = (accountName == null) ? "" : accountName.Trim();

            this.tAttorneyNo.Text = Item.AttorneyNo;
...
          

        } 
        #endregion

        #region protected override void ComposeData()
        /// <summary>
        /// Осуществляет заполнение объекта на основе данных формы
        /// </summary>
        protected override void ComposeData()
        {
            // валидация уже прошла, можем заполнять данные
            Item.AttorneyNo = tAttorneyNo.Text.Trim();
            Item.Description = tDescription.Text.Trim();
...
        } 
        #endregion

        #region protected override bool ValidateData(out string message)
        /// <summary>
        /// Осуществляет проверку введенных данных пользователем на форме.
        /// </summary>
        /// <param name="message">Сообщение, которое отображается в случае ошибок заполнения</param>
        /// <returns>True, если проверка пройдена успешно, False, если есть замечания</returns>
        protected override bool ValidateData(out string message)
        {
            // проверяем, чтобы были заданы необходимые поля

            bool ok = true;
            message = "";

            if (!UIValidator.ValidateEmptyString(tAttorneyNo, lAttorneyNo, false))
            {
                ok = false;
                message += "Необходимо указать номер доверенности" + Environment.NewLine;
            }

            if (!UIValidator.ValidateEmptyString(tDescription, lDescription, false))
            {
                ok = false;
                message += "Необходимо указать полномочия доверенного лица" + Environment.NewLine;
            }
...

            return ok;
        }
        #endregion

    }


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

The designer could not be shown for this file because none of the classes within it can be designed. The designer inspected the following classes in the file: fCustomer --- The base class 'Entities.Forms.fParent' could not be loaded. Ensure the assembly has been referenced and that all projects have been built.

Хотя компиляция проекта и его работа работают корректно.

Буду благодарен любым советам, комментариям и ссылкам
Re: Параметризованные формы
От: Gremlin2 http://www.fb2library.net/
Дата: 17.11.10 08:56
Оценка: 3 (1)
Здравствуйте, Аноним, Вы писали:

А>Доброго времени суток!


А>При разработке столкнулись со следующей проблемой:

А>есть достаточно большое количество бизнес-сущностей, которые хранятся в базе данных.
А>При необходимости работы с ними — бизнес-объекты загружаются из базы данных.

А>Все бизнес-объекты (для удобства) реализуют простейший интерфейс:


...

А>И здесь начинаются проблемы.

А>Дизайнер не загружается, и пишет следующую проблему

А>The designer could not be shown for this file because none of the classes within it can be designed. The designer inspected the following classes in the file: fCustomer --- The base class 'Entities.Forms.fParent' could not be loaded. Ensure the assembly has been referenced and that all projects have been built.


А>Хотя компиляция проекта и его работа работают корректно.


А>Буду благодарен любым советам, комментариям и ссылкам


Есть такое, победить тоже легко. При помощи пустого промежуточного класса:

public partial class fCustomer : fCustomerForm
{
    // остальной код формы
}

public class fCustomerForm :  fParent <Customer>
{
}
Re: Параметризованные формы
От: _FRED_ Черногория
Дата: 17.11.10 10:02
Оценка:
Здравствуйте, Аноним, Вы писали:

А>При разработке столкнулись со следующей проблемой:

А>есть достаточно большое количество бизнес-сущностей, которые хранятся в базе данных.
А>При необходимости работы с ними — бизнес-объекты загружаются из базы данных.

А>Все бизнес-объекты (для удобства) реализуют простейший интерфейс:




А>Этот интерфейс реализуется следующим образом




А>Теперь необходимо вытащить объект на форму для пользовательского редактирования.

А>Алгоритм работы с формой стандартный:

А>- отобразить данные сущности

А>- применить настройки безопасности для текущего пользователя
А>после того, как пользователь внесет изменения выполнить следующий алгоритм:

А>- провести валидацию данных

А>- "собрать" объект на основе тех данных, которые внес пользователь
А>- осуществить сохранение данных

А>Класс-формы выглядит следующим образом


А вы можете показать, для чего в классе формы нужно знать именно тип Customer и почему не достаточно одного интерфейса
Из примера этого не понять.

Вообще, я бы рекомендовал попрактиковаться в ООП: задизайнить так, что бы ничего лишнего небыло. В рассмотренном примере generic кажется избыточным.
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Параметризованные формы
От: kirill_kgz Киргизия  
Дата: 17.11.10 10:38
Оценка:
Здравствуйте, Gremlin2, Вы писали:

Спасибо, помогло!

G>Есть такое, победить тоже легко. При помощи пустого промежуточного класса:


G>
G>public partial class fCustomer : fCustomerForm
G>{
G>    // остальной код формы
G>}

G>public class fCustomerForm :  fParent <Customer>
G>{
G>}
G>
Re[2]: Параметризованные формы
От: kirill_kgz Киргизия  
Дата: 17.11.10 10:44
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>А вы можете показать, для чего в классе формы нужно знать именно тип Customer и почему не достаточно одного интерфейса

_FR>Из примера этого не понять.

Чтобы мы могли при сохранении данных и их отображении на форме обращаться на прямую к свойствам, которые есть только у класса.
Интерфейс IEntity определяет только один int ID {get; set;} и метод для сохранения Save ().
А интерфейс IEntity реализуют достаточно разнородные классы: например Customer, а также, например, Account (параметры банковского счета). И у них практически ничего общего, кроме указанного ID.

Напишите пожалуйста пример, когда будет достаточно только интерфейса для отображения данных, и как их в этом случае отображать на форме

_FR>Вообще, я бы рекомендовал попрактиковаться в ООП: задизайнить так, что бы ничего лишнего небыло. В рассмотренном примере generic кажется избыточным.
Re[3]: Параметризованные формы
От: _FRED_ Черногория
Дата: 17.11.10 12:00
Оценка: 2 (1)
Здравствуйте, kirill_kgz, Вы писали:

_FR>>А вы можете показать, для чего в классе формы нужно знать именно тип Customer и почему не достаточно одного интерфейса

_FR>>Из примера этого не понять.

_>Чтобы мы могли при сохранении данных и их отображении на форме обращаться на прямую к свойствам, которые есть только у класса.


В том-то и дело: fParent-у знать про Customer ни разу не обязательно: детали нужны только уже в fCustomer. Поэтому я бы сделал как-то так (вообще, код кажется очень не правильным, но это лучше обсуждать в Архитектуре: сейчас же только о замене дженериков):
    public partial class fParent : Form
    {
        protected virtual IEntity ItemCore { get; }

        protected virtual void SaveData()
        {
            ItemCore.Save();
        }

        protected DialogResult EditItemCore(bool showModal)
        {
            this.FillElements();
            this.ApplySecuritySettings();

            if (showModal)
                return this.ShowDialog();
            else
            {
                this.MdiParent = UIManager.MDIForm;
                this.Show();
                return DialogResult.OK;
            }
        }
    }


    public partial class fCustomer : fParent <Customer>
    {
        private Customer Item { get; set; }

        protected override IEntity ItemCore { get { return Item; } }

        public DialogResult EditItem(Customer item, bool showModal)
        {
            Item = item;
            return EditItemCore(showModal);
        }
    }


В чём цимес: fParent ничего не знает про конкретные IEntity, и только конкретная формула знает, с чем работает. Но при этом не нужно тянуть за собой дженерик-параметры в иерархии (а где-то ещё какой параметр понадобится? а кому-то надо будет обобщённо работать с fParent и ему придётся знать/получать тип entity).

В данном случае вы используете дженерики исключительно для генерации кода (по сути — двух свойств в fCustomer в моём примере (хранящего данные Item и предоставляющего их для базы ItemCore)). Для кодогенерации, если она действительна нужна, лучше использовать специально заточенные средства: можно или создать шаблон страницы или, если первого способа не достаточно, прикрутить T4.

_>Напишите пожалуйста пример, когда будет достаточно только интерфейса для отображения данных, и как их в этом случае отображать на форме


Для обобращения конкретные типы, вообще говоря, не нужны: используйте биндинг. В вашем же примере конкретики в базе и так нету, она в наследниках.
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Параметризованные формы
От: Кирилл Барышников Киргизия  
Дата: 18.11.10 11:09
Оценка:
Здравствуйте, _FRED_, Вы писали:

Спасибо, Ваш код в любом случае будет лучше.

_FR>В данном случае вы используете дженерики исключительно для генерации кода (по сути — двух свойств в fCustomer в моём примере (хранящего данные Item и предоставляющего их для базы ItemCore)). [b]Для кодогенерации, если она действительна нужна, лучше использовать специально заточенные средства: можно или создать шаблон страницы или, если первого способа не достаточно, прикрутить T4.


Можно ссылку, где прочитать про Т4 и что это такое.
Re[5]: Параметризованные формы
От: _FRED_ Черногория
Дата: 18.11.10 13:10
Оценка: 2 (1)
Здравствуйте, Кирилл Барышников, Вы писали:

КБ>Можно ссылку, где прочитать про Т4 и что это такое.


Code Generation and Text Templates (MSDN)
T4: Text Template Transformation Toolkit (Oleg Sych)

Большего, пожалуй, и не нужно. Разве что http://www.visualt4.com/
Help will always be given at Hogwarts to those who ask for it.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.