首先在EF代码中删除会导致导航属性设置为null并为空

时间:2011-07-21 15:17:19

标签: entity-framework-4.1 ef-code-first

当我首先使用EF代码执行删除时,我发现了一些有趣的东西。我使用以下域模型:

public class User
{
    public virtual long Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Playlist> Playlists { get; set; }
}

public class Playlist
{
    public virtual long Id { get; set; }
    public virtual string Title { get; set; }
    public virtual User User { get; set; }
    public virtual ICollection<Track> Tracks { get; set; }
}

public class Track
{
    public virtual long Id { get; set; }
    public virtual string Title { get; set; }
    public virtual Playlist Playlist { get; set; }
}

使用以下命令配置模型:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().HasMany(x => x.Playlists).WithRequired(x => x.User).Map(x => x.MapKey("UserId"));
    modelBuilder.Entity<Playlist>().HasMany(x => x.Tracks).WithRequired(x => x.Playlist).Map(x => x.MapKey("PlaylistId"));
}

我使用通用存储库:

public virtual void Delete(T entity)
{
    Database.Set<T>().Remove(entity);
}

我也有一个看起来像的dto:

public class PlaylistDTO
{
    public PlaylistDTO(Playlist playlist)
    {
        Id = playlist.Id;
        Title = playlist.Title;
        User = playlist.User.Name;
    }
}

在我的一项服务中,我尝试执行以下操作:

public PlaylistDTO Delete(long id)
{
    Playlist playlist = playlistRepository.GetById(id);
    playlistRepository.Delete(playlist);
    unitOfWork.Commit();

    return PlaylistDTO(playlist);
}

此代码失败。当我通过调试器时,我发现了一些有趣的东西。我调用playlistRepository.Delete时,导航属性(User和Tracks)分别设置为null和empty。然而,播放列表会留在内存中。因此,当我将播放列表传递给DTO时,代码将在尝试访问playlist.User.Name时失败。我想将此数据传递给客户端以显示验证。

这种行为是否正确?这是设计吗?

1 个答案:

答案 0 :(得分:7)

这就是EF的工作原理。问题是你的Playlist与其他关系形成实体图,而EF使用非常简单的规则来跟踪实体图:必须跟踪图中的所有实体 - 不能引用未跟踪的实体。我没有给你参考这条规则的描述,这只是我的观察,但我没有发现任何一个例外。

编辑:更新版本 - 我刚刚检查了内部实施,并且在调用删除

期间确实无关联

所以你的代码发生了什么。

  • 您已将Playlist标记为已删除
  • EF将删除操作传递给执行修复的状态管理器 - 它将使所有关系为空
  • 您保存了对数据库的更改
  • 由于Playlist没有级联删除,所有相关对象仍未删除
  • 保存更改后,EF会在内部接受并将更改跟踪器设置为当前状态
  • 因为当前状态Playlist不存在(在数据库中删除),所以它与上下文分离
  • 分离破坏了实体图,EF通过修改两端的导航属性来修复它

负责从System.Data.Objects.EntityEntry.Delete(doFixup)归零的代码(doFixup为真) - 该类是内部的:

if (doFixup && (base.State != EntityState.Deleted))
{
    this.RelationshipManager.NullAllFKsInDependentsForWhichThisIsThePrincipal();
    this.NullAllForeignKeys();
    this.FixupRelationships();
}

在您的方案中,这应该有简单的解决方法 - 在删除实体之前创建DTO。