c# - Entity Framework DBContext 全局缓存?

标签 c# linq caching entity-framework-4 dbcontext

(EF4.1 - 4.0 框架)

网络上的大多数代码示例都规定了 Entity Framework 的最佳实践;他们说将您对 DBContext 的使用包装在一个 using block 中,以确保无状态操作。即便如此,我还是收到了似乎是共享缓存错误。

错误

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

环顾四周,当有人在许多调用中共享 DBContext 的全局实例时,就会出现这种情况的示例。

然而,我在第二次调用位于静态数据访问层服务类中的以下函数时收到此消息。

public static void UpdateRollout(Rollout rollout)
        {

               using (ITAMEFContext db = new ITAMEFContext(ConnectionStrings.XYZConnectionString))
                {
                    db.Configuration.ProxyCreationEnabled = false;
                    db.Configuration.LazyLoadingEnabled = false;

                    FixUp(rollout);


                    db.Rollouts.Attach(rollout);
                    db.Entry(rollout).State = System.Data.EntityState.Modified;

                    db.SaveChanges();

                    //db.Entry(rollout).State = System.Data.EntityState.Detached;

                }

}



private static void FixUp(Rollout rollout)
        {
            // ensure manual fixup of foreign keys
            if (rollout.RolloutState != null)
                rollout.FK_RolloutState_ID = rollout.RolloutState.ID;
            if (rollout.Lead != null)
                rollout.RolloutLead_FK_User_ID = rollout.Lead.ID;
        }

EFContext 是通过引用 edmx 模型的 EF 4.x DBContext Fluent Generator 生成的。

edmx model picture

看起来像这样。

public partial class ITAMEFContext : DbContext
{
    static ITAMEFContext()
    {
        Database.SetInitializer<ITAMEFContext>(null);
    }

    public ITAMEFContext() : base("name=ITAMEFContext")
    {
        this.Configuration.LazyLoadingEnabled = false;

    }

    public ITAMEFContext(string nameOrConnectionString) : base(nameOrConnectionString)
    {

    }

    public ITAMEFContext(string nameOrConnectionString, DbCompiledModel model) : base(nameOrConnectionString, model)
    {

    }

    public ITAMEFContext(DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection)
    {

    }

    public ITAMEFContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection)
    {

    }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
        modelBuilder.Configurations.Add(new Asset_Mapping());
        modelBuilder.Configurations.Add(new AssetAllocation_Mapping());
        modelBuilder.Configurations.Add(new AssetAssignee_Mapping());
        modelBuilder.Configurations.Add(new AssetAssigneeType_Mapping());
        modelBuilder.Configurations.Add(new AssetDeAllocation_Mapping());
        modelBuilder.Configurations.Add(new AssetState_Mapping());
        modelBuilder.Configurations.Add(new AssetType_Mapping());
        modelBuilder.Configurations.Add(new Department_Mapping());
        modelBuilder.Configurations.Add(new Location_Mapping());
        modelBuilder.Configurations.Add(new ManagementGroup_Mapping());
        modelBuilder.Configurations.Add(new Role_Mapping());
        modelBuilder.Configurations.Add(new Rollout_Mapping());
        modelBuilder.Configurations.Add(new RolloutState_Mapping());
        modelBuilder.Configurations.Add(new ServiceArea_Mapping());
        modelBuilder.Configurations.Add(new Software_Mapping());
        modelBuilder.Configurations.Add(new SoftwareType_Mapping());
        modelBuilder.Configurations.Add(new SubTeam_Mapping());
        modelBuilder.Configurations.Add(new sys_UserLock_Mapping());
        modelBuilder.Configurations.Add(new Team_Mapping());
        modelBuilder.Configurations.Add(new User_Mapping());
        modelBuilder.Configurations.Add(new WorkingMethod_Mapping());
    }

    public DbSet<Asset> Assets { get; set; }
    public DbSet<AssetAllocation> AssetAllocations { get; set; }
    public DbSet<AssetAssignee> AssetAssignees { get; set; }
    public DbSet<AssetAssigneeType> AssetAssigneeTypes { get; set; }
    public DbSet<AssetDeAllocation> AssetDeAllocations { get; set; }
    public DbSet<AssetState> AssetStates { get; set; }
    public DbSet<AssetType> AssetTypes { get; set; }
    public DbSet<Location> Locations { get; set; }
    public DbSet<Department> Departments { get; set; }
    public DbSet<ManagementGroup> ManagementGroup { get; set; }
    public DbSet<Role> Roles { get; set; }
    public DbSet<ServiceArea> ServiceAreas { get; set; }
    public DbSet<SubTeam> SubTeams { get; set; }
    public DbSet<Team> Teams { get; set; }
    public DbSet<User> User { get; set; }
    public DbSet<WorkingMethod> WorkingMethods { get; set; }
    public DbSet<Rollout> Rollouts { get; set; }
    public DbSet<RolloutState> RolloutStates { get; set; }
    public DbSet<Software> Softwares { get; set; }
    public DbSet<SoftwareType> SoftwareTypes { get; set; }
    public DbSet<sys_UserLock> sys_UserLock { get; set; }
}

我希望能够根据需要多次从我的 BL 层调用 UpdateRollout。 UI 将需要保留作为先前获取的列表的一部分返回的 POCO Rollout 实体图。

Rollout 和所有其他实体都是纯 POCO,不需要上下文跟踪。

我读到,一旦 using block 处理了 ITAMEFContext,任何上下文缓存/跟踪都会被删除。然而,在同一应用程序域中,似乎有某种全局缓存在 DBContext 的任何实例之下?老实说,到目前为止,EF 似乎比对分层应用程序使用良好的旧存储过程更有效。

POCO。

public partial class Rollout
{
    public Rollout()
    {
        this.AssetAssignees = new HashSet<AssetAssignee>();
    }

    public int ID { get; set; }
    public string Name { get; set; }
    public int RolloutLead_FK_User_ID { get; set; }
    public string EmailContacts { get; set; }
    public System.DateTime Schedule { get; set; }
    public int FK_RolloutState_ID { get; set; }
    public Nullable<int> NotificationDays { get; set; }
    public string Notes { get; set; }

    public virtual ICollection<AssetAssignee> AssetAssignees { get; set; }
    public virtual User Lead { get; set; }
    public virtual RolloutState RolloutState { get; set; }
}

编辑:

映射。

 internal partial class Rollout_Mapping : EntityTypeConfiguration<Rollout>
{
    public Rollout_Mapping()
    {                   
        this.HasKey(t => t.ID);     
        this.ToTable("Rollout");
        this.Property(t => t.ID).HasColumnName("ID");
        this.Property(t => t.Name).HasColumnName("Name").IsRequired().HasMaxLength(50);
        this.Property(t => t.RolloutLead_FK_User_ID).HasColumnName("RolloutLead_FK_User_ID");
        this.Property(t => t.EmailContacts).HasColumnName("EmailContacts").HasMaxLength(500);
        this.Property(t => t.Schedule).HasColumnName("Schedule");
        this.Property(t => t.FK_RolloutState_ID).HasColumnName("FK_RolloutState_ID");
        this.Property(t => t.NotificationDays).HasColumnName("NotificationDays");
        this.Property(t => t.Notes).HasColumnName("Notes");
        this.HasRequired(t => t.Lead).WithMany(t => t.Rollouts).HasForeignKey(d => d.RolloutLead_FK_User_ID);
        this.HasRequired(t => t.RolloutState).WithMany(t => t.Rollouts).HasForeignKey(d => d.FK_RolloutState_ID);
    }
}

最佳答案

我遇到了一个非常相似的问题,和你一样,我认为是某种全局缓存导致了这个问题。

我的用例是这样的:

  1. 使用新的 DbContext,在我的数据库上设置一些测试数据,然后处理 DbContet
  2. 对我的应用程序运行系统测试
  3. 将数据库重置为基线状态(我在 EF 之外执行此操作)
  4. 从第 1 步开始重复下一个系统测试

第一次测试一切正常,但在第二次测试中出现重复键错误。

这让我困惑了一段时间,直到我意识到我用来构建我的一些测试数据实体的工厂方法正在将它们创建为静态对象;第二次循环时,只要我将这些静态实体添加到上下文中,这些实体的完整对象图就会重新添加,所以当我稍后添加其他实体时,它们已经在那里了。

这是一个简化的例子......

循环 1:

  1. 创建对象 A(静态)。保存更改 [数据库现在包含 A]
  2. 创建与对象 A 有关系的对象 B(非静态)。保存更改 [数据库现在包含 A 和 B]
  3. 重置数据库[数据库现在不包含任何内容]

循环 2:

  1. 创建对象 A(静态的,因此实际上并未重新创建。仍然包含对 B 的引用,即使它不在数据库中)。保存更改 [数据库现在包含 A 和 B]
  2. 创建对象 B(非静态)。保存更改。 [繁荣!重复键,因为 B 已经在数据库中]

解决方案: 我更改了我的工厂方法,这样我的所有实体都不是静态的。问题解决了。

关于c# - Entity Framework DBContext 全局缓存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16308124/

相关文章:

c# - 如何检查每个列表成员的属性值是否相同

c# - Asp.Net - 无法创建类型的常量值。在此上下文中仅支持原始类型或枚举类型

C#:如果一个类有两个构造函数,那么这些构造函数共享一些代码的最佳方式是什么?

c# - 如何关闭事件的datareader c#

C# 从一个位置跳到另一个位置

.net - REDIS 耐久性 ?如何自动过期数据?

iphone - NSData dataWithContentsOfURL 缓存

c# - 如何确定 HTTP 响应是否完整

c# - 使用特定值解决 MemberChangeConflict

php - Elasticsearch缓存/性能问题