Лучший "шаблон" для уровня доступа к данным для бизнес-объекта

Я пытаюсь выяснить самый чистый способ сделать это.

В настоящее время у меня есть объект клиента:

public class Customer
{
    public int Id {get;set;}
    public string name {get;set;}
    public List<Email> emailCollection {get;set}
    public Customer(int id)
    {
        this.emailCollection = getEmails(id);
    }
}

Тогда мой объект электронной почты также довольно простой.

public class Email
{
    private int index;
    public string emailAddress{get;set;}
    public int emailType{get;set;}
    public Email(...){...}
    public static List<Email> getEmails(int id)
    {
        return DataAccessLayer.getCustomerEmailsByID(id);
    }
}

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

Итак, где и как я могу улучшить это?

Должен ли мой DataAccessLayer возвращать DataTable и оставить его для объекта Email для анализа и возврата списка обратно клиенту?

Я думаю, что "Factory", вероятно, является неправильным словом, но должен ли я иметь другой тип EmailFactory, который берет DataTable из DataAccessLayer и возвращает объект List to Email? Я думаю, что подобные звуки излишни...

Является ли это даже правильной практикой использование моего Email.getEmails(id) как статического метода?

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

Спасибо.


Последующие действия

Я создал рабочий пример, в котором мой объект Domain/Business извлекает запись клиента по идентификатору из существующей базы данных. Файлы сопоставления xml в nhibernate действительно опрятные. После того, как я последовал за учебником по настройке сессий и заводов-репозитариев, потянув записи базы данных было довольно просто.

Однако я заметил огромный успех.

Мой оригинальный метод состоял из хранимой процедуры в БД, которая была вызвана объектом DAL, которая проанализировала набор результатов в моем доменном/бизнес-объекте.

Я выполнил свой первоначальный метод, взяв 30 мс, чтобы захватить одну запись клиента. Затем я синхронизировал метод nhibernate при захвате 3000 мс, чтобы захватить одну и ту же запись.

Я что-то упустил? Или есть только много накладных расходов, используя этот маршрут nhibernate?

В противном случае мне нравится чистота кода:

protected void Page_Load(object sender, EventArgs e)
{
    ICustomerRepository repository = new CustomerRepository();
    Customer customer = repository.GetById(id);
}

public class CustomerRepository : ICustomerRepository
        {
            public Customer GetById(string Id)
            {
                using (ISession session = NHibernateHelper.OpenSession())
                {
                    Customer customer = session
                                        .CreateCriteria(typeof(Customer))
                                        .Add(Restrictions.Eq("ID", Id))
                                        .UniqueResult<Customer>();
                    return customer;
                }
            }
        }

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

public class NHibernateHelper
    {
        private static ISessionFactory _sessionFactory;
        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    Configuration cfg = new Configuration();
                    cfg.Configure();
                    cfg.AddAssembly(typeof(Customer).Assembly);
                    _sessionFactory = cfg.BuildSessionFactory();
                }
                return _sessionFactory;
            }

        }

        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    }

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

Все еще открыт для предложений!


Обновление.

Я отказываюсь от своего маршрута ORM/NHibernate. Я обнаружил, что производительность слишком медленная, чтобы оправдывать ее использование. Основные запросы клиентов слишком затянуты для нашей среды. 3 секунды по сравнению с суб-вторыми ответами слишком много.

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

Однако, сыграв с NHibernate на прошлой неделе, это отличный инструмент! Это просто не совсем соответствует моим потребностям в этом проекте.

Ответы

Ответ 1

Если у вас сейчас есть работа, зачем с ней работать? Не похоже, что вы определяете какие-либо конкретные потребности или проблемы с кодом, как есть.

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

Ответ 2

Я реализовал слой DAL, в основном делая то, что NHibernate делает, но вручную. Что NHibernate делает, это создать класс Proxy, который наследуется от вашего объекта Domain (который должен иметь все его поля, помеченные как виртуальные). Весь код доступа к данным переходит в переопределения свойств, на самом деле он довольно элегантен.

Я несколько упростил это, если мои репозитории заполнили сами простые свойства и использовали только прокси для загрузки Lazy. В итоге я создал набор таких классов:

public class Product {
  public int Id {get; set;}
  public int CustomerId { get; set;}
  public virtual Customer Customer { get; set;}
}
public class ProductLazyLoadProxy {
  ICustomerRepository _customerRepository;
  public ProductLazyLoadProxy(ICustomerRepository customerRepository) {
    _customerRepository = customerRepository;
  }
  public override Customer {
    get {
      if(base.Customer == null)
        Customer = _customerRepository.Get(CustomerId);
      return base.Customer
    }
    set { base.Customer = value; }
  }
}
public class ProductRepository : IProductRepository {
  public Product Get(int id) {
    var dr = GetDataReaderForId(id);
    return new ProductLazyLoadProxy() {
      Id = Convert.ToInt(dr["id"]),
      CustomerId = Convert.ToInt(dr["customer_id"]),
    }
  }
}

Но после того, как я написал около 20 из них, я просто сдался и узнал NHibernate, с Linq2NHibernate для запросов и FluentNHibernate для настройки в настоящее время дорожные блоки ниже, чем когда-либо.

Ответ 3

Это может быть слишком радикальным для вас и на самом деле не решает вопрос, но как насчет полного отказа от уровня данных и выбора ORM? Вы сэкономите много избыточности кода, которые будут потрачены на неделю или около того на DAL.

В стороне, шаблон, который вы используете, похож на шаблон репозитория, вроде. Я бы сказал, что ваши варианты

  • Объект службы в вашем классе электронной почты - скажем, EmailService - создается в конструкторе или в свойстве. Доступ через экземпляр, например email.Service.GetById(id)
  • Статический метод по электронной почте, такой как Email.GetById(id), который является аналогичным подходом
  • Полностью отдельный статический класс, который в основном является классом фасадов, например, MailManager со статическими методами, такими как EmailManager.GetById(int)
  • Шаблон ActiveRecord, в котором вы имеете дело с экземпляром, например email.Save() и email.GetById()

Ответ 4

Скорее всего, ваше приложение имеет настройку логики домена в сценарии транзакций. Для реализаций .NET, использующих транзакцию script Мартин Фаулер рекомендует использовать шаблон table data gateway..NET обеспечивает хорошую поддержку этого шаблона, потому что шаблон шлюза данных таблицы отлично работает с набором записей, который Microsoft реализует с помощью классов типа DataSet.

Различные инструменты в среде Visual Studio должны повысить производительность. Тот факт, что DataSets может быть легко привязан к различным элементам управления (например, DataGridView), делает его хорошим выбором для приложений, управляемых данными.

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