不总是填充的类属性。什么是最佳做法?

时间:2010-09-28 15:43:31

标签: .net

我有一个模拟在线广告系列的课程,即人们从联盟网站点击进入该网站,我们需要记录所有这些点击和所做的任何购买。这个类有通常的废话 - 名称,开始日期,结束日期等 - 我们需要返回,我有一个构造函数来填充这些值。一切都非常标准,一切都非常好。我已经完成了所有标准的GetAll和GetById函数,它们使用该构造函数来填充对象。

但是,在一种情况下,我需要返回一系列广告系列及其总计,例如:总点击次数,总购买次数等。这些点击/购买存储在单独的数据库表中,总计将使用SQL aggragate函数计算。我可以调用一个函数来获取总点击次数,然后调用另一个函数来获取每个广告系列的总购买次数,但这意味着每个广告系列的两次额外数据库调用都会返回。

另一种选择是在搜索广告系列时始终返回总计,但这会对数据库进行不必要的工作,因为点击表可能会很快变得非常大并且在其上执行聚合函数会不会很好理念。

我终于想到声明公共属性totalClicks和totalPurchases,它们没有在构造函数中填充,但只有当你调用特定的GetTheTotals函数时才会这样。这很棒,但这意味着当您访问活动对象的实例时,您可以尝试objCampaign.totalClicks,即使它可能未设置。

我知道我可以返回一个数据集,但从面向对象的角度来看,这似乎并不正确。知道正确的方法在这里吗?

4 个答案:

答案 0 :(得分:1)

这里的一种可能性是Lazy<T>,允许对数据库进行JIT调用。请务必启用正确的线程安全选项。

答案 1 :(得分:0)

GetTheTotals函数使用延迟加载。

但是,不是让用户调用它,而是在未填充时从totalClicks调用它。

我还会在GetTheTotalsGetTotalClicks中拆分GetTotalPurchases

您的代码可能如下所示:

private int? _totalClicks; // private storage
public int TotalClicks {
   get {
      if (!_totalClicks.HasValue) {
         // Optional thread syncing code
         _totalClicks = GetTotalClicks();
      }
      return _totalClicks.Value;
   }
}

答案 2 :(得分:0)

我会声明GetTotalClicksGetTotalPurchases方法,并检查该值是否已在函数内填充。

答案 3 :(得分:0)

延迟加载和批量加载通常是相互对立的。当您预计访问详细信息字段将是不常见的操作时,前者非常有用 - 并且允许您避免不必要的工作。但是,如果获取一个对象与所有对象的详细信息的成本较低 - 那么简单地批量加载所有信息可能是有意义的。

您可以合并这些行为 - 并实施批量延迟加载。

为此,您需要将所有携带数据的对象绑定在一起,以便在要求其汇总总数时,您可以获取所有对象的总计。

此类问题的常见设计是将聚合详细信息存储在单独的帮助程序对象中(通常根据某些主键组织到字典中)。最初没有填充辅助对象 - 但是当一个请求来自一个实例时,您加载所有实例的数据。

以下是我的意思的骨架示例:

class CampaignInfo
{
   public int Key { get; set; }
   public string Name { get; set; }
   public DateTime StartDate { get; set; }
   public DateTime EndDate { get; set; }
   // etc...

   // initialized on construction all CampaignInfo instances share reference
   private AggregateDetails _details; 

   public int TotalClicks
   {
       get { return _details.TotalClicksFor( this.Key ); }
   }

   public int TotalPurchases
   {
       get { return _details.TotalPurchasesFor( this.Key ); }
   }
}

class AggregateDetails
{
    private class AggregateInfo
    {
        public int TotalClicks;
        public int TotalPurchases;
        // etc...
    }

    private readonly Dictionary<int,AggregateInfo> _cachedInfo;

    public int TotalClicksFor( int key )
    {
        if( _cachedInfo == null )
            LoadAggregateInfo(); // loads aggregates for all campaigns
        return _cachedInfo[key].TotalClicks;
    }

    public int TotalPurchasesFor( int key )
    {
        if( _cachedInfo == null ) 
            LoadAggregateInfo(); // loads aggregates for all campaigns
        return _cachedInfo[key].TotalPurchases;
    }

    // etc...
}

显然,这段代码需要错误和异常处理,一些管理并发的方法(以便调用获取数据是线程安全的),一种跟踪加载数据的活动密钥的方法,一种查询数据库的机制,等等上。但它应该让您了解如何构建您的实现以充分利用这两个世界。