实体框架:如何在更新或删除“通用”父项时删除相关实体

时间:2016-07-14 12:34:04

标签: c# entity-framework entity-framework-6 cascading-deletes

我有一个Product类,其中包含Picture属性。其他类也可能有图片,例如Customer可以有Picture

Product.cs:

public class Product
{
    public Picture Picture { get; set; }
} 

Customer.cs:

public class Customer
{
    public Picture Picture { get; set; }
} 

Picture.cs:

public class Picture
{
    public string Type { get; set; }

    public byte[] Content { get; et; }
}

图片始终是可选的,即产品和客户可能有也可能没有图片。但是,图片与唯一的“父”实体(产品或客户)唯一链接。没有父母,图片就没有用处。请注意,Picture与父类之间没有链接,因为此父级可以是多种类型(产品或客户)。

另一个要求是我希望通过急切或延迟加载来完全控制图片是否加载。例如。检索产品列表时,我不希望获取图片,但如果要求提供单个产品,则应包含图片。

我的问题:如何配置实体框架以便:

  • 删除父(产品或客户)记录时删除图片记录
  • 每当更新父记录且新版本不包含图片时,都会删除图片记录
  • 只要使用新图片更新父级,就会替换图片记录(删除+创建记录)
  • 图片是可选的
  • 始终控制图片是否已加载

我正在使用流畅的API 来定义关系。目前,没有任何级联删除。

public class ProductMap : EntityTypeConfiguration<Product>
{
    public ProductMap()
    {
        ToTable("PRODUCTS");

        HasKey(x => x.Id);

        HasOptional(x => x.Picture).WithOptionalDependent().Map(m => m.MapKey("PICTUREID"));
    }
}

我尝试使用WithRequired,但这会产生错误,因为PictureProduct/Customer没有链接或外键。

4 个答案:

答案 0 :(得分:3)

你可以这样做:

public abstract class Picture
{
    public int Id {get;set;}
    public string Type { get; set; }
    public byte[] Content { get; set; }
}

public class ProductImage:Picture
{
    public int ProductId {get;set;}
    public virtual Product Product {get;set;}
}
public class CustomerImage:Picture
{
   public int CustomerId {get;set;}
   public virtual Customer Customer{get;set;}
}

然后你可以像这样配置: 恩。对于产品:

HasOptional(x=>x.ProductImage)
 .withRequired(x=>x.Product)
 .HasForeignKey(x=>x.ProductId); //cascade on delete is default true

通过这种方式,您可以在需要时加载图片,如果删除产品项目,图像也将被删除。 图像是可选的,可以替换。 在更新时,您必须指定新图像或删除旧图像。

希望这个替代方案可以帮助您选择您想要的。

答案 1 :(得分:2)

我认为您可以将Picture配置为ComplexType,这将在父表中生成Picture类列的属性。

这意味着:

  • 每当删除父项时,都会删除相应的Picture
  • 每当使用新的Picture数据(更新的图片数据或没有数据)更新父级时,父表中的相应列也会更新。

此处描述了如何将Picture实体配置为ComplexTypehttp://weblogs.asp.net/manavi/entity-association-mapping-with-code-first-part-1-one-to-one-associations

这是您需要做的最低限度:

public class MyDbContext:DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.ComplexType<Picture>();
    }
}

如果您运行migration,那么它可能会生成类似于以下内容的迁移:

    public override void Up()
    {
        CreateTable(
            "dbo.Customers",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Picture_Type = c.String(),
                    Picture_Content = c.Binary(),
                })
            .PrimaryKey(t => t.Id);

        CreateTable(
            "dbo.Products",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Picture_Type = c.String(),
                    Picture_Content = c.Binary(),
                })
            .PrimaryKey(t => t.Id);

    }

此外,您还可以为ComplexType编写流畅的配置,如下所示:

public class PictureConfig : ComplexTypeConfiguration<Picture>
{
    public PictureConfig()
    {
        Property(t => t.Type).HasColumnName("PictureType");
        Property(t => t.Content).HasColumnName("PictureContent");
    }
}

并在DbContext中添加此配置:

public class MyDbContext:DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //modelBuilder.ComplexType<Picture>();
        modelBuilder.Configurations.Add(new PictureConfig());
    }
}

如果你现在add-migration,它可能如下所示:

    public override void Up()
    {
        CreateTable(
            "dbo.Customers",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    PictureType = c.String(),
                    PictureContent = c.Binary(),
                })
            .PrimaryKey(t => t.Id);

        CreateTable(
            "dbo.Products",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    PictureType = c.String(),
                    PictureContent = c.Binary(),
                })
            .PrimaryKey(t => t.Id);

    }

希望这有帮助。

根据评论进行更新: 这部分更新并没有真正为您提供您想要的内容,而只是提出了一个建议。

我认为您正在寻找以下关系:

Product---1-----1---Picture ---1------1---Customer
|- Id               |- Id                 |- Id
|- PictureId?                             |- PictureId?

也就是说,您可以在父类上保留nullable PictureId,并在图片更改时更改此PictureId

缺点:您需要自己管理数据操作活动(CRUD)。

优点:这样您可以完全控制何时加载图像,因为加载父图像不会自动加载Picture。此外,这使您可以将Picture与数据库分离并使用某种blob存储(可能在云端等)。

答案 2 :(得分:0)

我也在StackOverflow上找到了这个,也许它可以帮到你:

modelBuilder.Entity<Product>()
    .HasKey(c => c.Id)
    .HasRequired(c => c.User)
    .WithRequiredDependent(c => c.UserProfile)
    .WillCascadeOnDelete(true);

我认为您需要稍微更改一下代码,但它应该可以正常工作......

原帖:EF Code First Fluent API - Cascade Delete

也许这也可以帮到你:Cascade delete using Fluent API

答案 3 :(得分:0)

添加调用以删除要删除产品的图片。如果您在多个地方执行此操作,则重新考虑封装此操作。我发现EF配置不够透明,但在DeleteProduct方法或DeleteCustomer方法中简单的删除调用很容易阅读和理解。