最佳的数据访问层到业务对象的“模式”是什么?

16

我正在尝试找出最简洁的方法来完成这个操作。

目前我有一个客户对象:

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迭代结果集并创建新的Email对象,并将它们添加到List中,完成后返回该列表。
那么我应该在哪里和如何改进呢?
我应该让我的DataAccessLayer返回一个DataTable,然后由Email对象解析并返回一个List返回给客户端吗?
我猜“工厂”可能不是正确的词,但我是否应该有另一种类型的EmailFactory,它从DataAccessLayer获取一个DataTable,并向Email对象返回一个List?这听起来好像有点多余…
将Email.getEmails(id)方法作为静态方法,这是适当的做法吗?
也许我只是试图找到并应用最佳“模式”来完成通常是简单任务的工作。
谢谢。
跟进:
我创建了一个可行的示例,在现有数据库中,我的Domain/Business对象通过ID提取客户记录。nhibernate中的xml映射文件非常棒。在我按照教程设置会话和存储库工厂之后,获取数据库记录非常直观。
然而,我注意到性能损失很大。
我的原始方法包括在DB上的存储过程,由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;
                }
            }
        }

我按照这个示例创建了一个帮助类来管理Session,也许这就是我遇到开销的原因?

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秒,那将是一个巨大的优势。但是,如果我在做一些奇怪的事情,这是一次性的初始设置成本,那么如果性能与在DB上执行存储过程一样好,那可能值得一试。

仍然欢迎建议!


更新。

我放弃了ORM/NHibernate路线。我发现它的性能太慢了,无法证明使用它的价值。我们的环境下基本的客户查询需要太长时间。比起亚秒级响应,3秒钟太长了。

如果我们想要慢查询,我们就可以继续使用我们目前的实现。重写的想法是为了显著提高查询速度。

然而,在过去的一周里尝试了NHibernate后,我认为它是一个非常好的工具!只是它不完全符合我的项目需求。


Barry- 是的,目前它运行得非常好。我唯一能想到的是让我的DataAccessLayer返回一个DataTable,这样我就可以在我的Business Layer(即Email对象)中进行验证检查,而不是在Data Access Layer中进行。 - Dave Baghdanov
1
你发布的例子是经典的“贫血领域模型”。根据你的例子,你甚至不需要一个业务对象。业务对象应该包含业务逻辑,而你的没有。 - O.O
4个回答

6
如果你现在的配置可以工作,为什么要去改动它呢?听起来你并没有发现代码存在任何特定需求或问题。
我相信 OO 类型的人们可能会集结在一起,提出各种重构建议以确保正确的职责和角色得到尊重,甚至有人可能会试图硬塞一两个设计模式。但是你现在的代码很简单,并且似乎没有任何问题 - 我会建议保持不变。

我认为自己是面向对象(OO)的人,但我完全同意。当前的解决方案非常简单,即使我也能理解它,这绝对是一个优秀的品质! - Treb
2
一个建议是在业务对象中添加更多的“业务”行为。似乎上面没有正确使用业务对象。 - O.O

5

我基本上通过手动实现NHibernate的方式来实现了一个数据访问层(DAL)。NHibernate的做法是创建一个代理类,该类继承自你的领域对象(Domain object),(该对象应该将其所有字段标记为virtual)。所有的数据访问代码都在属性重写中完成,这实际上相当优雅。

我简化了这个过程,通过使我的仓库(Repositories)填充简单的属性并仅使用代理进行延迟加载。我最终得到了一组像这样的类:

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进行配置,现在的路障比以往任何时候都要低。


2
这可能对你来说有些激进,也不能真正解决问题,但考虑完全放弃数据层并选择ORM如何?这将节省大量的代码冗余,而花费一周左右的时间在DAL上则不会带来这种好处。
除此之外,你正在使用的模式有点像仓储模式。我建议你有以下几个选择:
  • 在你的Email类中创建一个服务对象 - 比如EmailService - 在构造函数或属性中实例化。通过类似email.Service.GetById(id)这样的实例进行访问。
  • Email类上的静态方法,比如Email.GetById(id),这是一种类似的方法
  • 完全分离的静态类,基本上是一个门面类,例如EmailManager,具有像EmailManager.GetById(int)这样的静态方法
  • ActiveRecord模式,其中你正在处理一个实例,比如email.Save()和email.GetById()

2
您的应用程序很可能在事务脚本中设置了其域逻辑。对于使用事务脚本的.NET实现,Martin Fowler建议使用表数据网关模式。.NET对这种模式提供了良好的支持,因为表数据网关模式非常适合记录集,而Microsoft则是通过其DataSet类型类实现的。
Visual Studio环境中的各种工具应该可以提高您的生产力。DataSets可以轻松地绑定到各种控件(如DataGridView),使其成为数据驱动应用程序的不错选择。
如果您的业务逻辑比几个验证更复杂,那么领域模型将成为一个不错的选择。请注意,领域模型带有一整套不同的数据访问要求!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接