当我首先使用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时失败。我想将此数据传递给客户端以显示验证。
这种行为是否正确?这是设计吗?
答案 0 :(得分:7)
这就是EF的工作原理。问题是你的Playlist
与其他关系形成实体图,而EF使用非常简单的规则来跟踪实体图:必须跟踪图中的所有实体 - 不能引用未跟踪的实体。我没有给你参考这条规则的描述,这只是我的观察,但我没有发现任何一个例外。
编辑:更新版本 - 我刚刚检查了内部实施,并且在调用删除
期间确实无关联所以你的代码发生了什么。
Playlist
标记为已删除Playlist
没有级联删除,所有相关对象仍未删除Playlist
不存在(在数据库中删除),所以它与上下文分离负责从System.Data.Objects.EntityEntry.Delete(doFixup)
归零的代码(doFixup
为真) - 该类是内部的:
if (doFixup && (base.State != EntityState.Deleted))
{
this.RelationshipManager.NullAllFKsInDependentsForWhichThisIsThePrincipal();
this.NullAllForeignKeys();
this.FixupRelationships();
}
在您的方案中,这应该有简单的解决方法 - 在删除实体之前创建DTO。