2012-01-28 59 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更改为较旧的列。