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 以便:
- 删除parent(产品或客户)记录时删除图片记录
- 每当更新parent记录且新版本不包含图片时删除图片记录
- 只要 parent 更新了新图片,图片记录就会被替换(删除 + 创建记录)
- 图片可选
- 始终控制图片是否加载
我正在使用 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 或从 Picture
到 Product/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
实体配置为 ComplexType
:http://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
这样你可以在需要的时候加载图片,如果你删除商品项,图片也会被删除。
图片是可选的,可以替换。
更新时,您必须指定新图像或删除旧图像。
希望这个备选方案能帮助您准确选择您想要的内容。
我有一个 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 以便:
- 删除parent(产品或客户)记录时删除图片记录
- 每当更新parent记录且新版本不包含图片时删除图片记录
- 只要 parent 更新了新图片,图片记录就会被替换(删除 + 创建记录)
- 图片可选
- 始终控制图片是否加载
我正在使用 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 或从 Picture
到 Product/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
实体配置为 ComplexType
:http://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
这样你可以在需要的时候加载图片,如果你删除商品项,图片也会被删除。 图片是可选的,可以替换。 更新时,您必须指定新图像或删除旧图像。
希望这个备选方案能帮助您准确选择您想要的内容。