Entity Framework:如何在更新或删除 'generic' parent 时删除相关实体

Entity Framework: how to delete related entity upon update or delete of 'generic' parent

我有一个 Product class,其中包含一个 Picture 属性。其他 classes 也可能有图片,例如一个 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; }
}

图片始终是可选的,即产品和客户可能有也可能没有图片。然而,一张图片被唯一地 linked 到一个 'parent' 实体(产品或客户)。没有 parent,Picture 就没有存在的意义。请注意,没有 link 从图片到 parent class,因为此 parent 可以是多种类型(产品或客户)。

另一个需求是我想通过 eager 或 lazy loading 完全控制图片是否加载。例如。检索产品列表时,我不希望获取图片,但如果请求单个产品,则应包括图片。

我的问题:我如何配置 Entity Framework 以便:

我正在使用 fluent 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 但这会产生错误,因为没有 link 或从 PictureProduct/Customer 的外键。

我也在 Whosebug 上找到了这个,也许它会对你有所帮助:

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

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

原文Post:EF Code First Fluent API - Cascade Delete

也许这对您也有帮助:

我认为您可以将 Picture 配置为 ComplexType,这将使 Picture class 列的属性在父 table.

这意味着:

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

此处描述了如何将 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?

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

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

优点:通过这种方式您可以完全控制何时加载图像,因为加载父图像不会自动加载 Picture。此外,这使您能够将 Picture 从数据库中分离出来,并使用某种 blob 存储(可能在云端左右)。

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

您可以按如下方式进行:

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

这样你可以在需要的时候加载图片,如果你删除商品项,图片也会被删除。 图片是可选的,可以替换。 更新时,您必须指定新图像或删除旧图像。

希望这个备选方案能帮助您准确选择您想要的内容。