.net 类属性,这些属性不是';它不总是有人居住。什么是最佳实践?

.net 类属性,这些属性不是';它不总是有人居住。什么是最佳实践?,.net,.net,我有一个模拟在线活动的类,即人们从附属网站点击网站,我们需要记录所有这些点击和任何购买。这个类有通常的垃圾名称,开始日期,结束日期等等,我们需要返回它们,我有一个构造函数来填充这些值。都很标准,都很好。我已经完成了所有标准的GetAll和GetById函数,它们使用该构造函数来填充对象 但是,在一种情况下,我需要返回活动列表及其总计,例如总点击量、总购买量等。这些点击量/购买量存储在单独的DB表中,总计将使用SQL Agragate函数计算。我可以调用一个函数,该函数将获得总点击量,然后调用另一

我有一个模拟在线活动的类,即人们从附属网站点击网站,我们需要记录所有这些点击和任何购买。这个类有通常的垃圾名称,开始日期,结束日期等等,我们需要返回它们,我有一个构造函数来填充这些值。都很标准,都很好。我已经完成了所有标准的GetAll和GetById函数,它们使用该构造函数来填充对象

但是,在一种情况下,我需要返回活动列表及其总计,例如总点击量、总购买量等。这些点击量/购买量存储在单独的DB表中,总计将使用SQL Agragate函数计算。我可以调用一个函数,该函数将获得总点击量,然后调用另一个函数,在我们循环遍历每个活动时获取每个活动的总购买量,但这意味着每个活动返回两个额外的数据库调用

另一种选择是在搜索活动时始终返回总数,但这会给数据库带来不必要的工作,因为clicks表可能会很快变大,对其执行聚合函数不是一个好主意

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


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

使用
GetTheTotals
函数的延迟加载`

但是,当它未填充时,您可以从
totalClicks
调用它,而不是让用户调用它

我还将在
GetTotalClicks
GetTotalPurchases
中拆分
GetTotalPurchases

您的代码可能如下所示:

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

这里的一种可能性是,允许对数据库进行JIT调用。确保启用正确的线程安全选项。

我将声明
GetTotalClicks
gettotalpurchaes
方法,并检查该值是否已在函数中填充。

延迟加载和批加载通常是相互对立的。当您预计访问详细信息字段将是一个不频繁的操作时,前者非常有用,并允许您避免不必要的工作。但是,如果获取一个对象的详细信息的成本比获取所有对象的详细信息的成本低,那么只需批量加载所有信息就可以了

您可以将这些行为结合起来,并实现批量延迟加载。

要做到这一点,您需要将所有携带数据的对象绑定在一起,这样当一个对象被要求提供其聚合总计时,您就可以获取所有对象的总计

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

下面是我的意思的一个简单例子:

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...
}
class活动信息
{
公共int密钥{get;set;}
公共字符串名称{get;set;}
公共日期时间起始日期{get;set;}
公共日期时间结束日期{get;set;}
//等等。。。
//构造时初始化所有活动信息实例共享引用
私有聚合详细信息;
公众点击总数
{
获取{return _details.totalclicks for(this.Key);}
}
公共采购
{
获取{return _details.TotalPurchasesFor(this.Key);}
}
}
类聚合详细信息
{
私有类聚合信息
{
公众点击;
公共采购;
//等等。。。
}
专用只读词典_cachedInfo;
公共整数总计点击(整数键)
{
如果(_cachedInfo==null)
LoadAggregateInfo();//加载所有活动的聚合
返回_cachedInfo[key]。单击总数;
}
公共int TotalPurchasesFor(int键)
{
如果(_cachedInfo==null)
LoadAggregateInfo();//加载所有活动的聚合
返回_cachedInfo[key].TotalPurchases;
}
//等等。。。
}

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

将这些作为方法而不是属性是有意义的,但您也不想访问数据库两次。我认为您不需要访问数据库两次,只需检查以前是否调用过
GetTheTotals
。我不建议使用属性,因为属性给类用户的印象是,访问它们在处理时间方面很便宜,但这里不是这样。我最初打算惰性地加载它们,但当我运行实际返回这些字段的报告时,我会为报告中的每一个不理想的活动调用数据库3次。好吧,你可能会懒散地获取其中任何一个,然后急切地获取所有这些活动的JIT。换句话说,如果有人要求其中一个聚合字段,您将计算并缓存这三个字段。我不推荐这种特定的实现,因为它不是线程安全的。只使用Lazy来实现它的设计目的会更容易、更安全,这正是它的目的。嗨,LBuskin。这是一个非常有趣的方法。谢谢你的帮助