2012-01-28 85 views
1

我有一個這樣的實體:ASP.NET Web應用程序+實體框架4.0併發檢查

public class Player 
{ 
    [Required] 
    [Key] 
    public int Id { get; set; } 
    [Required] 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string NativeCountry { get; set; } 
    [ConcurrencyCheck] 
    public DateTime LastModified { get; set; } 
    public virtual int TeamId { get; set; } 

    //navigational property 
    public virtual Team Team { get; set; } 
    public virtual ICollection<Tournament> Tournaments { get; set; } 
} 

這是我如何配置球員實體:

public PlayerConfiguration() 
{ 
    Property(e => e.Id).IsRequired(); 
    Property(e => e.FirstName).IsRequired().IsConcurrencyToken(true); 
    Property(e => e.NativeCountry).IsOptional(); 
    Property(e => e.LastModified).IsOptional().IsConcurrencyToken(true); 

    HasRequired(e => e.Team).WithMany(s => s.Players).HasForeignKey(f => f.TeamId); 
} 

重寫OnModelCreating

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    Configuration.ValidateOnSaveEnabled = false; 
    Configuration.LazyLoadingEnabled = true; 

    modelBuilder.Configurations.Add(new PlayerConfiguration()); 
    modelBuilder.Configurations.Add(new TeamConfiguration()); 
    modelBuilder.Configurations.Add(new TournamentConfiguration()); 

    modelBuilder.Entity<Player>().ToTable("Player"); 
    modelBuilder.Entity<Team>().ToTable("Team"); 
    modelBuilder.Entity<Tournament>().ToTable("Tournament"); 

    base.OnModelCreating(modelBuilder); 
} 

某處我這樣做來更新播放器:

db.Entry<Player>(player).State = System.Data.EntityState.Modified; 
try 
{ 
    db.SaveChanges(); 
} 
catch (DbUpdateConcurrencyException ex) 
{ 

} 

當我嘗試使用兩個瀏覽器同時更新任何給定的播放器時,沒有任何反應。我不想使用TimeStamp註釋,因爲它會花費我一個額外的 列。如何使用現有的DateTime LastModified列來跟蹤併發性。 我甚至嘗試將FirstName(和其他)作爲ConcurrencyToken,但是再次沒有發生。 這個[ConcurrencyCheck]如何在asp.net web應用程序中工作? 請幫忙..

+0

此實體的併發列的值是否在數據庫中發生更改?它的工作方式是將作爲併發標記的屬性的值與數據庫中的值進行比較,如果值相同,則意味着數據庫中的實體自從讀取後沒有更改,因此它是好,保存。另一方面,如果值不匹配,則意味着實體發生了變化,您可能會覆蓋更改。時間戳理想的原因是數據庫每次更新行時都會修改該值。 – Pawel 2012-01-28 12:05:51

+0

另請注意,如果值未由數據庫更新,則每當更改屬性值時都會有併發異常,因爲它不再與數據庫中的內容匹配。 – Pawel 2012-01-28 12:06:51

+0

Pawel,我在一個靜態字段中保留了舊的(原始的)LastModified值,但沒有編寫比較原始和當前的代碼。我的意思是,這是實體框架的工作權利?請幫忙 – Manish 2012-01-30 11:41:16

回答

2

它看起來像你的代碼不會改變LastModified屬性,所以你仍然使用相同的併發令牌。這是人們使用Timestamp列的原因,因爲時間戳是由數據庫自動處理的,您不必處理它。

要使用併發令牌,您的實體必須遵循非常嚴格的規則。

  • 您的實體必須保持併發標記的舊值(如果是ASP.NET,它需要向客戶端發送併發令牌,並使用修改後的數據接收它)。
  • 如果您不使用數據庫生成的併發令牌(時間戳或行版本),則每次要更改記錄時都必須手動設置它
  • 如果使用分離的實體,則只能設置新的令牌您將實體上下文,否則你會在您每次嘗試保存更新的數據

這裏時間得到異常的示例代碼,以驗證併發檢查工作原理:

class Program 
{ 
    static void Main(string[] args) 
    { 
     using (var context = new Context()) 
     { 
      context.Database.Delete(); 
      context.Database.CreateIfNotExists(); 

      context.Players.Add(new Player { FirstName = "ABC", LastName = "EFG", NativeCountry = "XYZ", LastModified = DateTime.Now}); 
      context.SaveChanges(); 
     } 

     using (var context = new Context()) 
     { 
      var player = context.Players.First(); 

      // Break here, go to database and change LastModified date 
      player.LastModified = DateTime.Now; 
      // If you changed LastModified date you will get an exception 
      context.SaveChanges(); 
     } 
    } 
} 
+0

@ Ladislav ..我試着做你說的話,但沒有奏效。我將原始的LastModified值保存在靜態字段中,並在保存時將其更新爲player.LastModified。但仍然沒有發生。我甚至將LastModified列創建爲byte [],並將其標記爲TimeStamp,甚至在每次更新時都會自動增加,但仍然沒有任何反應。如果我必須比較兩個值(在靜態字段中的LastModified和在Db中的當前LastModified),那麼它對我來說就不是很有成效。相同的代碼(具有適當的修改)在ASP.NET MVC3中完美工作,但不在這裏 – Manish 2012-01-30 11:39:53

+0

您無法使用原始值更新它。如果你這樣做,你會說沒有改變。有了時間戳,情況在ASP.NET中更加複雜,因爲您無法直接從代碼中設置它:http://stackoverflow.com/questions/5327649/entity-framework-optimistic-concurrency-exception-not-occuring/5327770 #5327770 – 2012-01-30 11:48:26

+0

拉迪斯拉夫感謝您的幫助..u幫助我解決問題。下面是我的文章,展示了我是如何實現它的。 – Manish 2012-01-30 13:16:22

0

這是我做的到使其工作:

球員實體:

[ConcurrencyCheck] 
public DateTime LastModified { get; set; } 
在我的代碼

,形式的onLoad,調用此函數:

private static DateTime TimeStamp; 
protected void LoadData() 
    { 
     Player player = new PlayerRepository().First(plyr => plyr.Id == Id); 
     if (player != null) 
     { 
      TimeStamp = player.LastModified; 
      //rest of the code 
     } 
    } 

,並於即保存按鈕的點擊,同時更新實體:

protected void btnSave_Click(object sender, EventArgs e) 
    { 
     var playerRepo = new PlayerRepository(); 
     var teamRepo = new TeamRepository(); 

      if (Page.IsValid) 
      { 
      Player player; 
      if (this.Id == 0)//add 
      { 
       player = new Player(); 
       //rest of the code 
      } 
      else //edit 
       player = playerRepo.First(p => p.Id == Id); 

      player.LastModified = DateTime.Now; 

      //custom logic 
      if (this.Id == 0) // add 
      { 
       playerRepo.Add(player); 
       playerRepo.SaveChanges(); 
      } 
      else // edit 
      { 
       try 
       { 
        playerRepo.OriginalValue(player, "LastModified", TimeStamp); 
        playerRepo.SaveChanges(); 
       } 
       catch (DbUpdateConcurrencyException ex) 
       { 
        //custom logic 
       } 

      } 
     } 

和AbstractRepository中的OriginalValue是這樣的:

public void OriginalValue(TEntity entity, string propertyName, dynamic value) 
    { 
     Context.Entry<TEntity>(entity).OriginalValues[propertyName] = value; 
    } 

所以明確我必須將ConcurrencyCheck標記列的OriginalValue更改爲較舊的列。