2017-04-10 85 views
1

我有一个统计的次数计数器的观点是呈现:巨大的性差异

public class News 
{ 
    [Key] 
    public int Id { get; set; } 
    public DateTime DateCreated { get; set; } 
    public DateTime DatePostedOn { get; set; } 
    public DateTime DateModified { get; set; } 
    public string Title { get; set; } 
    public string Description { get; set; } 
    public string Meta { get; set; } 
    public string UrlSlug { get; set; } 
    public bool Popular { get; set; } 
    public int Position { get; set; } 
    public int Views { get; set; }   // <---- COUNTER 
} 

每次加载页面时,我有+1到浏览次数:

public virtual ActionResult Detail(string urlSlug) 
{ 
    News news = db.News 
        .Include("Category") 
        .Where(n => n.UrlSlug.Equals(urlSlug)) 
        .FirstOrDefault(); 

    NewsDetailVM vm = Mapper.Map<News, NewsDetailVM>(news); 

    vm.RelatedNews = db.News 
         .Where(r => r.Category.Id == news.Category.Id) 
         .Where(r => r.Hidden == false) 
         .Where(r => r.Id != news.Id) 
         .OrderByDescending(r => r.DateCreated) 
         .Take(3) 
         .Select(r => new NewsRelatedVM() 
         { 
          Title = r.Title, 
          Date = r.DateCreated, 
          UrlSlug = r.UrlSlug, 
          CategoryName = r.Category.Name, 
          CategoryURLSlug = r.Category.UrlSlug, 
          PictureURL = r.Pictures.Where(p => p.Type == 2).FirstOrDefault().Filename 
         }) 
         .ToList(); 

    vm.CategoriesList = db.Categories 
         .Where(c => c.Channel == 1) 
         .Project().To<NewsListCategoryItemVM>() 
         .ToList(); 

    news.Views = news.Views + 1; // <-- COUNTER UPDATE 

    db.SaveChanges(); 

    return View(vm); 
} 

问题是,例如在星期六,我在当天发布的新闻有6464个,Google分析为新闻控制器呈现了2359个综合浏览量。

从哪里来这个差异?我在柜台做错了什么?

回答

3

你不能依靠实体框架来更新一个这样的计数器,因为它会将最新值存入内存,在内存中修改它,然后将其保存到数据库中。

如果多人同时击中了你的动作方法,那么他们每个人可以将相同的值加1并将其保存回数据库。在更新数据库时,您需要对数据库中的值执行锁定。

要么或者允许数据库做它最好的,这是管理更新 这样,你可以通过使用存储过程,而不是如一个我刚刚敲打了如下实现这一点:

CREATE PROCEDURE usp_increment_count (@newsId int) 

    UPDATE dbo.News SET Views = Views + 1 WHERE id = @newsId 
    GO 

然后调用使用实体框架,从你的代码的程序:

SqlParameter newsId = new SqlParameter("@newsId", news.Id); 
db.Database.ExecuteSqlCommand("usp_increment_count @newsId", newsId); 

我希望这会有所帮助。

+0

嗨卢克,谢谢!但是,如果操作只是基于选择来呈现新闻,是不是一个非常快速的操作,可以在一次访问中进行更新并避免出现问题? – Patrick

+1

我知道你在说什么,但是EF的体重很重。您正在执行一些数据的选择,然后是更多的数据,在选择数据和增加计数器之间将两个结果集映射到对象。增加计数器的唯一保证方法是让数据库引擎为你处理它:)。 – Luke

+0

您是否能够达到此目的? – Luke

7

几乎不可能说差距的确切原因是什么,但有几个可能的罪魁祸首。首先,遗传算法可能会忽略机器人,特别是GoogleBot。但是,您的方法不会将僵尸工具索引到您的网站。例如,每次GoogleBot点击此视图时,它都会在数据库中增加,但不会在GA中增加。您需要通过蜘蛛访问权限排除此视图,或者找到某种方式来过滤机器人的请求。

其次,遗传算法可能会滤除重复匹配。例如,假设用户登陆视图,然后点击刷新按钮50次。你的数据库数量增加了50倍,但GA可能仍然只是将其作为一个统一的浏览量。一般来说,你应该通过记录REMOTE_IP来节制你的计数器,并且只在该IP在15分钟的某个时间段内没有访问该页面时才递增。那么,你至少不会记录每一次刷新。

最后,在这种情况下计数将始终关闭,因为并非每个客户端都支持或启用了JavaScript(意味着GA无法跟踪这些客户端),并且客户端的隐私设置也可能会阻止GA跟踪。然而,无论您是否希望它,此数据库计数都会计算每个请求。

+0

嗨克里斯,谢谢!这似乎很复杂,以避免或最大限度地减少差异... :( – Patrick

+0

任何建议或示例? – Patrick

+0

嗨!感谢您的帮助。我现在与SP解决方案;) – Patrick

2

计数不是完全相同的测量,也不是100%准确。

如果存在并发访问,您的实体框架代码将不起作用。见Concurrency with EF。这篇文章向你展示了如何修改你的表格和程序来使计数准确。使用存储过程的解决方案看起来可以与您的示例一起使用,但不是一个通用的解决方案,因为除非在存储过程中完成所有更改,否则它也会失败。这在EF6中更容易。

谷歌分析有它自己的一套问题。有很多文章给出细节,这里是一个sample article。不要在客户端代码中打折编码问题。

您可能会考虑的解决方案是创建一个新表来跟踪访问。这可以毫不费力地解决EF并发问题。新表格行至少应包含主键和新闻标识列。只需在每次阅读时添加一行。然后使用单独的查询访问计数,使用适当的索引应该足够快。这种方法的优点是您可以添加额外的信息,如访问时间,访问来源以及任何其他您需要调和或证明您的数量的信息。

您可能试图帮助解决您的问题的另一件事是分析Web服务器日志。这里有很多选项,维基百科是一个很好的开始,搜索'web服务器日志分析'的地方。再次,这些测量略有不同的东西,并会给出不同的数字。

+0

嗨!谢谢你的帮助。我现在正在使用SP解决方案;) – Patrick