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

Posted

技术标签:

【中文标题】数据访问层到业务对象的最佳“模式”【英文标题】:Best "pattern" for Data Access Layer to Business Object 【发布时间】:2010-10-13 07:06:47 【问题描述】:

我正在尝试找出最干净的方法。

目前我有一个客户对象:

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);
    

那么我的 Email 对象也很基本。

public class Email

    private int index;
    public string emailAddressget;set;
    public int emailTypeget;set;
    public Email(...)...
    public static List<Email> getEmails(int id)
    
        return DataAccessLayer.getCustomerEmailsByID(id);
    

DataAccessLayer 当前连接到数据库,并使用 SqlDataReader 遍历结果集并创建新的 Email 对象并将它们添加到完成后返回的 List。

那么我可以在哪里以及如何改进呢?

我是否应该让我的 DataAccessLayer 返回一个 DataTable 并将其留给 Email 对象进行解析并将 List 返回给客户?

我猜“Factory”可能是错误的词,但我是否应该有另一种类型的 EmailFactory,它从 DataAccessLayer 获取一个 DataTable 并将一个 List 返回给 Email 对象?我想那种听起来是多余的......

将我的 Email.getEmails(id) 作为静态方法是否是正确的做法?

我可能只是在尝试寻找最佳“模式”并将其应用到通常是简单的任务中。

谢谢。


跟进

我创建了一个工作示例,其中我的域/业务对象通过 id 从现有数据库中提取客户记录。 nhibernate 中的 xml 映射文件非常整洁。在我按照教程设置会话和存储库工厂后,提取数据库记录非常简单。

但是,我注意到性能受到了巨大影响。

我的原始方法由数据库上的存储过程组成,由 DAL 对象调用,该对象将结果集解析为我的域/业务对象。

我最初的方法是用 30 毫秒来获取一条客户记录。然后我将 nhibernate 方法计时为 3000 毫秒以获取相同的记录。

我错过了什么吗?还是使用这种休眠路由会产生很多开销?

否则我喜欢代码的简洁性:

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;
                
            
        

example I followed 让我创建了一个帮助类来帮助管理会话,也许这就是我得到这个开销的原因?

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 之后,它是一个很棒的工具!它只是不太适合我对这个项目的需求。

【问题讨论】:

Barry- 是的,它现在工作得很好。我唯一能想到的就是让我的 DataAccessLayer 返回一个 DataTable,这样我就可以在我的业务层(即电子邮件对象)中而不是在数据访问层中进行验证检查。 您发布的示例是经典的“贫血域模型”。根据您的示例,您甚至不需要业务对象。业务对象应该包含业务逻辑,而您的则没有。 【参考方案1】:

如果您的配置现在可以正常工作,为什么要搞砸呢?听起来您并没有确定代码的任何特定需求或问题。

我敢肯定,许多 OO 类型可能会挤在一起,并在这里提出各种重构建议,以便尊重正确的职责和角色,甚至有人可能会尝试硬塞一两个设计模式。但是您现在拥有的代码很简单,听起来没有任何问题 - 我会说离开它。

【讨论】:

我认为自己是一个面向对象的人,但我完全同意。现在的方案简单到连我都能看懂,绝对是好品质! 一个建议是向业务对象添加更多“业务”行为。好像上面的业务对象没有被正确使用。【参考方案2】:

我已经实现了一个 DAL 层,基本上是通过 NHibernate 所做的,但手动操作。 NHibernate 所做的是创建一个继承自域对象的代理类(它的所有字段都应标记为虚拟)。所有数据访问代码都进入属性覆盖,实际上它非常优雅。

我通过让我的存储库自己填写简单属性并仅使用延迟加载代理来简化这一点。我最终得到的是一组这样的类:

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 上花费一周左右会带来。

除此之外,您使用的模式有点类似于存储库模式。我会说你的选择是

Email 类中的服务对象 - 例如 EmailService - 在构造函数或属性中实例化。通过 email.Service.GetById(id) 等实例访问 Email 上的静态方法,例如 Email.GetById(id),这是一种类似的方法 一个完全独立的静态类,基本上是一个外观类,例如 EmailManager,具有像 EmailManager.GetById(int) 这样的静态方法 您正在处理实例的 ActiveRecord 模式,例如 email.Save() 和 email.GetById()

【讨论】:

【参考方案4】:

您的应用程序很可能在transaction scripts 中设置了域逻辑。对于使用事务脚本的 .NET 实现,Martin Fowler 建议使用table data gateway 模式。 .NET 为这种模式提供了很好的支持,因为表数据网关模式非常适合 record set,Microsoft 使用其 DataSet 类型的类来实现。

Visual Studio 环境中的各种工具可以提高您的工作效率。 DataSet 可以轻松地与各种控件(如 DataGridView)进行数据绑定,这一事实使其成为数据驱动应用程序的理想选择。

如果您的业务逻辑比一些验证更复杂,domain model 将成为一个不错的选择。请注意,域模型带有一组完全不同的数据访问要求!

【讨论】:

以上是关于数据访问层到业务对象的最佳“模式”的主要内容,如果未能解决你的问题,请参考以下文章

在抽象访问时访问数据库中数据的最佳实践

设计模式数据访问对象模式

Java数据访问对象模式

Java数据访问对象模式

如何将远程数据访问(通过 http)逻辑与业务逻辑分开?

从 DAL 返回数据对象