更新时间:2022-11-16 10:34:52
这是EF的工作原理。问题是,您的播放列表
与其他关系形成实体图,EF使用非常简单的规则来跟踪实体图:图中的所有实体必须被跟踪 - 不能参考未跟踪的实体。我不给你参考这个规则的描述,这只是我的观察,但是我没有发现这个规则的任何一个例外。
编辑:更新版本 - 我刚刚检查内部实现和关键确实在调用时删除
所以你的代码发生了什么。
播放列表
标记为已删除播放列表
中没有级联删除所有相关对象仍未删除播放列表
的当前状态不存在(在数据库中已删除),因此已从上下文负责从 System.Data.Objects.EntityEntry.Delete(doFixup)
( doFixup
是true) - 类是内部的:
if(doFixup&&(base.State! =进入yState.Deleted))
{
this.RelationshipManager.NullAllFKsInDependentsForWhichThisIsThePrincipal();
this.NullAllForeignKeys();
this.FixupRelationships();
}
在您的场景中,应该有简单的解决方法 - 在删除实体之前创建DTO。
I noticed something interesting when I was performing a delete using EF code first. I use the following domain model:
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; }
}
The model is configured using:
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"));
}
I use a generic repository:
public virtual void Delete(T entity)
{
Database.Set<T>().Remove(entity);
}
I also have a dto that looks like:
public class PlaylistDTO
{
public PlaylistDTO(Playlist playlist)
{
Id = playlist.Id;
Title = playlist.Title;
User = playlist.User.Name;
}
}
In one of my services I am trying to do the following:
public PlaylistDTO Delete(long id)
{
Playlist playlist = playlistRepository.GetById(id);
playlistRepository.Delete(playlist);
unitOfWork.Commit();
return PlaylistDTO(playlist);
}
This code fails. When I stepped through the debugger I noticed something interesting. The moment I call playlistRepository.Delete the navigational properties (User and Tracks) get set to null and empty respectively. Playlist however stays in memory. So when I pass in the playlist to the DTO the code will fail when it is trying to access playlist.User.Name. I wanted to pass this data to the client to display a verification.
Is this behavior correct? Is this by design?
This is how EF works. The problem is that your Playlist
forms entity graph with other relations and EF uses very simple rule for tracking entity graphs: All entities in the graph must be tracked - there cannot be reference to entity which is not tracked. I don't give you reference to description of this rule, it is just my observation but I didn't find any single exception to this rule.
Edit: Updated version - I just checked internal implementation and relations are indeed nulled during calling Delete
So what happened in your code.
Playlist
as deletedPlaylist
all related objects remain undeletedPlaylist
is non existing (deleted in the database) it was detached from the contextThe code responsible for nulling from System.Data.Objects.EntityEntry.Delete(doFixup)
(doFixup
is true) - the class is internal:
if (doFixup && (base.State != EntityState.Deleted))
{
this.RelationshipManager.NullAllFKsInDependentsForWhichThisIsThePrincipal();
this.NullAllForeignKeys();
this.FixupRelationships();
}
In your scenario this should have simple workaround - create DTO before you delete entity.