public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
}
此时我们来进行一下一对一关系映射从产品角度出发:
public class ProductMap : EntityMappingConfiguration<Product>
{
Map(EntityTypeBuilder<Product> b)
{
b.ToTable();
b.HasKey(k => k.Id);
b.HasOne(o => o.Category).WithOne(o => o.Product);
}
}
此时我们通过 dotnet ef migrations add Initial 初始化就已经出现如下错误:
大概意思为未明确Product和Category谁是依赖项,未明确指定导致出现上述错误。而上述对于一对多关系则不会出现如此错误,仔细分析不难发现一对多已经明确谁是主体,而对于一对一关系二者为一一对应关系,所以EF Core无法判断其主体,所以必须我们手动去指定。此时我们若进行如下指定你会发现没有lambda表达式提示:
b.HasOne(o => o.Category) .WithOne(o => o.Product) .HasForeignKey(k=>k.)
还是因为主体关系的原因,我们还是必须指定泛型参数才可以。如下所示:
b.HasOne(o => o.Category) .WithOne(o => o.Product) .HasForeignKey<Category>(k => k.ProductId);
此时在Category上创建ProductId外键,同时会对ProductId创建如下的唯一非聚集索引:
CREATE UNIQUE INDEX [IX_Category_ProductId] ON [Category] ([ProductId]);
Many-Many RelationShip (多对多关系)多对多关系在EF Core之前版本有直接使用的方法如HasMany-WithMany,但是在EF Core中则不再提供对应的方法,想想多对多关系还是可以通过一对多可以得到,比如一个产品属于多个分类,而一个分类对应多个产品,典型的多对多关系,但是通过我们的描述则完全可以通过一对多关系而映射得到,下面我们一起来看看:
public class Product { public int Id { get; set; } public string Name { get; set; } public IEnumerable<ProductCategory> ProductCategorys { get; set; } }
public class Category { public int Id { get; set; } public string Name { get; set; } public int ProductId { get; set; } public IEnumerable<ProductCategory> ProductCategorys { get; set; } }
public class ProductCategory { public int ProductId { get; set; } public Product Product { get; set; } public int CategoryId { get; set; } public Category Category { get; set; } }
上述我们将给出第三个关联类即ProductCategory,将Product(产品类)和Category(分类类)关联到ProductCategory类,最终我们通过ProductCategory来进行映射,如下:
public class ProductCategoryMap : EntityMappingConfiguration<ProductCategory> { Map(EntityTypeBuilder<ProductCategory> b) { b.ToTable(); b.HasKey(k => k.Id); b.HasOne(p => p.Product) .WithMany(p => p.ProductCategorys) .HasForeignKey(k => k.ProductId); b.HasOne(p => p.Category) .WithMany(p => p.ProductCategorys) .HasForeignKey(k => k.CategoryId); } }
好了到了这里为止,关于三种映射关系我们介绍完了,是不是就此结束了,远远不是,下面我们再来其他属性映射。
键映射关于键映射中的外键映射上述已经讨论过,下面我们来讲讲其他类型键的映射。
备用键/可选键映射(HasAlternateKey)备用键/可选键可以为一个实体类配置除主键之外的唯一标识,比如在登录中用户名可以作为用户的唯一标识除了主键标识外,这个时候我们可以为UserName配置可选键,打个比方这样一个场景:一个用户只能购买一本书,在Book表中配置一个主键和用户Id(例子虽然不太恰当却能很好描述可选键的使用场景)
public class Book { public int Id { get; set; } public string UserId { get; set; } }
下面我们通过可选键来配置用户Id的映射