无法跟踪实体类型的实例,因为已经跟踪了另一个具有相同键值的实例

何为EntityState,如何解决Attach出现的跟踪问题

EntityState
之前使用EF,我们都是通过调用SaveChanges方法把增加/修改/删除的数据提交到数据库,但是上下文是如何知道实体对象是增加、修改还是删除呢?答案是通过EntityState枚举来判断的,我们看一个方法:

/// <summary>/// 查看实体状态
        /// </summary>private static void GetOneEntityToSeeEntityState()
        {
            using (var context = new DbContexts.DataAccess.BreakAwayContext())
            {
                var destination = context.Destinations.Find(4);
                EntityState stateBefore = context.Entry(destination).State;
                Console.WriteLine(stateBefore);
            }
        }

注:使用EntityState需添加引用system.data
跑下程序,输出结果为:Unchanged。从英文意思我们已经猜到一二:取出来的数据是Unchanged,那么添加、修改、删除自然也就是Added、Modified、Deleted了。我们在EntityState上按F12定位到其定义看看:

的确,当调用SaveChanges方法的时候,EF会根据EntityState这个枚举检测到实体的状态,然后执行相应的增/删/改操作。它们的具体意思分别为:

  • Detached:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态;
  • Unchanged:自对象加载到上下文中后,或自上次调用 System.Data.Objects.ObjectContext.SaveChanges() 方法后,此对象尚未经过修改;
  • Added:对象已添加到对象上下文,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法;
  • Deleted:使用 System.Data.Objects.ObjectContext.DeleteObject(System.Object) 方法从对象上下文中删除了对象;
  • Modified:对象已更改,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法。

跟踪问题
假设我们从数据库取出一个product对象并且new了一个和该数据库内product对象具有相同ID的product对象,
那么当我们修改完new的对象想要使用attach时就会出现报错:
*

无法跟踪实体类型的实例,因为已经跟踪了另一个具有相同键值的实例

那么这里是什么情况呢?
其实是因为取出的对象此时正在被EF跟踪,而我们想要attach的对象和该取出的对象具有相同的ID,此时就会出现EF无法使用attach的问题
那么如何解决呢?
正如上文所说,实体有很多状态,那么我们只需要将被取出的对象不再被跟踪即可。

db.Entry(entityFromDB).State = entityState.Detached

之后即可直接attach

更多.NET知识可以在我的笔记中学习:
ASP.NET Core RESTful风格学习总结(五万字持续更新)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我来为你介绍一个自行设计的数据库项目。 这个项目是一个简单的用户管理系统,它包含两个实体类:User和Role。其中,User包含用户的id、用户名、密码和角色等信息;Role包含角色的id和名称等信息。 首先,我们使用Spring Data JPA来现数据持久化。下面是User实体类的代码: ```java @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "username") private String username; @Column(name = "password") private String password; @ManyToOne @JoinColumn(name = "role_id") private Role role; // getter和setter方法 // ... } ``` 在这个实体类中,使用了@Entity注解将它标记为一个JPA实体类,@Table注解指定了该实体类对应的数据库表的名称,@Id注解指定了该实体类的主键,@GeneratedValue注解指定了主键的生成策略。另外,使用@Column注解来指定该属性对应的数据库字段,@ManyToOne和@JoinColumn注解来指定该属性与其他实体类的关联关系。 接下来是Role实体类的代码: ```java @Entity @Table(name = "role") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name") private String name; // getter和setter方法 // ... } ``` 这个实体类与User实体类类似,也使用了@Entity和@Table注解,以及@Id和@GeneratedValue注解。另外,它只包含了id和name两个属性。 接下来是自定义的Repository接口。我们将它命名为UserRepository: ```java @Repository public interface UserRepository extends JpaRepository<User, Long> { List<User> findByUsername(String username); } ``` 在这个接口中,我们使用了@Repository注解来标记它为一个Spring Data组件,继承了JpaRepository接口,并指定了实体类和主键的类。另外,我们自定义了一个findByUsername方法,用来根据用户名查询用户信息。 最后,我们使用Redis来现数据缓存。下面是一个简单的Redis配置类: ```java @Configuration @EnableCaching public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } } ``` 在这个配置类中,我们使用了@EnableCaching注解来启用Spring缓存机制,使用了@Bean注解来创建一个RedisTemplate实例。另外,我们指定了键和值的序列化方式。 最后,在Service层中,我们可以使用Spring Cache注解来现数据缓存。例如,下面是UserService中的一个方法: ```java @Service public class UserService { @Autowired private UserRepository userRepository; @Cacheable(value = "userCache", key = "#username") public User getUserByUsername(String username) { List<User> userList = userRepository.findByUsername(username); if (!userList.isEmpty()) { return userList.get(0); } return null; } } ``` 在这个方法中,我们使用了@Cacheable注解来指定缓存的名称和键值。如果缓存中已经存在该键值对应的数据,则直接返回缓存中的数据;否则,从数据库中查询数据,并将它存入缓存中。 这就是一个简单的使用Spring Data JPA和Redis进行数据整合的项目。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王也枉不了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值