【问题标题】:Where do objects merge/join data in a 3-tier model?对象在 3 层模型中在哪里合并/连接数据?
【发布时间】:2011-02-28 04:02:18
【问题描述】:

这可能是一个简单的 3 层问题。我只是想确保我们为此使用最佳实践,而且我对这些结构还不是很熟悉。

我们有 3 层:

  • GUI:用于表示层的 ASP.NET(第一个平台)
  • BAL:业务层将在 C# 中处理网络服务器上的逻辑,因此我们都可以将其用于 webforms/MVC + webservices
  • DAL:数据层中的 LINQ to SQL,返回 BusinessObjects 而不是 LINQ。
  • DB:SQL 将是 Microsoft SQL-server/Express(尚未决定)。

让我们考虑一下我们有一个 [Persons] 数据库的设置。他们都可以有多个 [Address] 并且我们有所有 [PostalCode] 和相应城市名称等的完整列表。

交易是我们从其他表格中加入了很多细节。

{Relations}/[表格]

  • [Person]:1 --- N:{PersonAddress}:M --- 1:[Address]
  • [地址]:N --- 1:[邮政编码]

现在我们要为 Person 构建 DAL。 PersonBO 的外观应该如何以及连接何时发生? 获取所有城市名称和可能的地址公关是业务层问题吗?人?或者 DAL 是否应该在将 PersonBO 返回给 BAL 之前完成所有这些?

Class PersonBO 
{
    public int ID {get;set;}
    public string Name {get;set;}
    public List<AddressBO> {get;set;} // Question #1
} 

// Q1:我们在返回 PersonBO 之前检索对象吗?它应该是一个数组吗?或者这对于 n-tier/3-tier 是完全错误的??

Class AddressBO 
{
    public int ID {get;set;}
    public string StreetName {get;set;}
    public int PostalCode {get;set;} // Question #2
} 

// Q2:我们是进行查找还是将邮政编码留待以后查找?

谁能解释以什么顺序拉哪些对象?建设性的批评是非常受欢迎的。 :o)

【问题讨论】:

    标签: c# linq-to-sql business-objects 3-tier n-tier-architecture


    【解决方案1】:

    你有点在重新发明轮子; ORM 已经为您解决了大部分问题,您会发现自己做起来有点棘手。

    像 Linq to SQL、Entity Framework 和 NHibernate 这样的 ORM 执行此操作的方式是一种称为延迟加载关联的技术(可以选择用急切加载覆盖)。

    当您提取Person 时,它不会加载Address,直到您特别要求它,此时会发生另一个到数据库的往返(延迟加载)。您还可以基于每个查询指定您希望为每个 人加载Address(急切加载)。

    从某种意义上说,对于这个问题,您基本上是在问您是否应该为PersonBO 执行AddressBO 的延迟加载或急切加载,答案是:都不行。没有一种单一的方法可以普遍有效。 默认情况下你应该延迟加载,这样你就不会做很多不必要的连接;为了实现这一点,您必须使用延迟加载机制构建您的PersonBO,该机制维护对 DAL 的一些引用。但是您仍然希望拥有预先加载的选项,您需要将其构建到您的“业务访问”逻辑中。

    如果您需要返回具有从许多不同表填充的特定属性的高度自定义的数据集,另一种选择是根本不返回 PersonBO,而是使用 Data Transfer Object (DTO)。如果您实现了默认的延迟加载机制,您有时可以将其替换为急切加载版本。


    仅供参考,数据访问框架中的惰性加载器通常使用关联本身的加载逻辑构建:

    public class PersonBO
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public IList<AddressBO> Addresses { get; set; }
    }
    

    这只是一个 POCO,魔法发生在实际的列表实现中:

    // NOT A PRODUCTION-READY IMPLEMENTATION - DO NOT USE
    
    internal class LazyLoadList<T> : IList<T>
    {
        private IQueryable<T> query;
        private List<T> items;
    
        public LazyLoadList(IQueryable<T> query)
        {
            if (query == null)
                throw new ArgumentNullException("query");
            this.query = query;
        }
    
        private void Materialize()
        {
            if (items == null)
                items = query.ToList();
        }
    
        public void Add(T item)
        {
            Materialize();
            items.Add(item);
        }
    
        // Etc.
    }
    

    (这显然不是生产级的,只是为了演示该技术;您从查询开始,直到必须时才具体化实际列表。)

    【讨论】:

    • 感谢您的时间和精力,我知道延迟加载原则,我认为更多的问题是在哪里放置什么代码或什么类应该执行什么操作才能成为“好层” “ 设计。我想“通过最佳实践”分离这些层,但我无法理解“PersonBAL.GetAll()”函数将如何返回数据以及这是否真的是“unLINQed”元素,因为它可能是一个请求数据的 Web 服务+ 当 GUI 层回发时,我们如何保留对象/ID 以保存/更新?
    • @Berggreen:我很确定我回答了这个问题;延迟加载代码位于集合本身或属性 getter 中(如果它是 1:1 关联)。如果您试图通过 Web 服务公开这种类型的 API,那么您做错了; Web 服务公开操作,而不是数据。话虽如此,对于那些需要通过 Web 服务返回深层嵌套关系的罕见情况,典型的解决方案是允许使用者在请求中指定要加载的关联,其余的保留为 @987654331 @/empty(无延迟加载)。
    • 好的,我可能只是没有完全理解你的答案,但我会标记为答案。 :o) 再次感谢!!
    • 我关于何时从数据库中“提取数据”的问题是关于邮政编码的。因为我可能需要在屏幕上向用户显示城市名称。但后来我需要确保当有人对城市名称具有编辑权限时它也可以更新。因为 citynames 不会改变,但让我们为其他类型的表考虑相同的结构。可能是电话号码、部门等。可能会发生变化。我想我需要不同的方法来处理不同的用法。一种用于阅读/呈现给最终用户,另一种用于管理员用户(更新)
    • 也许 BAL 应该决定用户的访问级别,或者也许 DAL 应该在保存数据之前过滤 BO/DTO 数据。在这种结构中思考时会出现这些问题。将什么“决定”放在哪里,从而传输多少数据以及何时传输。少即是好,及时性很好,如果做得好,延迟加载非常适合。只是计划,思考,讨论。 :o)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    • 2018-08-18
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    相关资源
    最近更新 更多