我有一个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,但这会产生错误,因为Picture
到Product/Customer
没有链接或外键。
答案 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
实体配置为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?
也就是说,您可以在父类上保留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方法中简单的删除调用很容易阅读和理解。