且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

EF CTP4级联删除多对多关系

更新时间:2023-02-15 18:38:29

截至EF CTP4,没有办法直接在流畅的API中打开多对多关联的级联删除。唯一的方法是将链接表明确地放在对象模型中:

  public class Project 
{
public int Id {get;组; }
public ICollection< ProjectXrefImage>图片{get;组;
}

public class ProjectXrefImage
{
[Key] [DataMember(Order = 1)]
public int ProjectId {get;组;

[Key] [DataMember(Order = 2)]
public int ImageId {get;组; }

public Project Project {get;组; }
public Image Image {get;组; }
}

public class Image
{
public int Id {get;组; }
public virtual ICollection< ProjectXrefImage>项目{get;组; }
}

public class MyContext:DbContext
{
public DbSet< Project>项目{get;组; }
public DbSet< Image>图片{get;组; }
public DbSet< ProjectXrefImage> ProjectsXrefImages {get;组; }
}

这样说,我个人不会这样做,将手动将它们打开在数据库中。




更新:



当您通过测试用例发现即使数据存储没有打开,代码首先将在客户端进行级联删除。这意味着,当您通过调用Remove()方法删除项目时,代码首先是足够聪明的,首先发送删除语句以从链接表(Images_Projects)中删除相关记录,之后它将发送另一个delete语句删除项目记录。我已经通过SQL Profiler验证了。



这就是为什么我们无法打开多对多关系的级联删除,因为我们不需要它!我上面解释的惯例会照顾我们!


I've created a many to many relationship using default conventions in EF CTP4 by defining an ICollection on both image and project entities.

The mapping table is created as below:

create table [dbo].[Images_Project] (
[Images_Id] [uniqueidentifier] not null,
[Project_Id] [uniqueidentifier] not null,
primary key ([Images_Id]));

Unfortunately when I delete a project it is not cascading deletes to the image mapping table.

I would expect EF to generate a key on both the Imanges_Id and Project_Id properties but this is not the case. How can I configure EF to delete the image mappings when a project is deleted? (only the image mapping record, not the image record)

Thanks

[Update]

Although cascade is apparently not possible, any idea why the following test passes:

    [Test]
    public void Can_delete_project_with_images()
    {
        var project = new Project { Title = "Test project" };
        var image = new Image { Title = "Some image" };
        project.AddImage(image);
        context.Set<Project>().Add(project);
        context.SaveChanges();

        object id = project.Id;
        object imageId = image.Id;

        var fromDb = context.Projects.Find(id);
        fromDb.ShouldNotBeNull();
        context.Set<Project>().Remove(fromDb);
        context.SaveChanges();

        var fromDb2 = context.Images.Find(imageId);
        fromDb2.ShouldNotBeNull();
        fromDb2.Title.ShouldEqual("Some image");
    }

As of EF CTP4, there is no way to directly turn on cascade deletes on Many to Many associations in fluent API. The only way to have that is to explicitly put the link table into the object model:

public class Project
{
    public int Id { get; set; }    
    public ICollection<ProjectXrefImage> Images { get; set; }
}

public class ProjectXrefImage 
{
    [Key][DataMember(Order = 1)]
    public int ProjectId { get; set; }

    [Key][DataMember(Order = 2)]
    public int ImageId { get; set; }

    public Project Project { get; set; }
    public Image Image { get; set; }
}

public class Image 
{
    public int Id { get; set; }
    public virtual ICollection<ProjectXrefImage> Projects { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Project> Projects { get; set; }
    public DbSet<Image> Images { get; set; }
    public DbSet<ProjectXrefImage> ProjectsXrefImages { get; set; }        
}

That said, I personally would not do this and will manually turn them on in the database.

Update:

As you discovered it through your test case, code first will take care of cascade delete on the client side even though it's not turned on on the data store. Which means, when you delete a Project by invoking Remove() method, code first is smart enough to first send a delete statement to get rid of the dependent record from the link table (Images_Projects) and after that it will send another delete statement to delete the project record. I've verify that with SQL Profiler.

That's why we can't turn on cascade deletes on many to many relationships, because we don't need it! The convention that I explained above will take care of that for us!