2017-04-10 89 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