-
EFCore入门
- 配置数据库连接字符串
- EFCore 如何自动获知数据库主键
- 将项目的变动更新到数据库
- 安装 EFCore 的数据库驱动
-
索引
- 索引 ( HasIndex ) / 唯一索引 ( IsUnique ) / 包含附加字段的索引 ( IncludeProperties) / 指定筛选条件的索引 ( HasFilter )
-
表之间的关系
- 主体实体 / 主体健 / 相关实体 / 外键
- 导航属性 / 集合导航属性 / 引用导航属性 / 反向导航属性
- 关系 / 自引用关系
- 不使用 FluentApi 定义时 -- 完全定义关系 / 单个导航属性
- FluentApi ( HasOne / WithMany / WithOne / HasMany )
- 什么情况下需要使用 FluentAPI
- 影子外键
-
InverseProperty 标注的用途
-
阴影属性
- 定义阴影字段
- 访问阴影字段
-
继承
- 非抽象类以及其子类只支持对应到单个表, 使用鉴别器列区分行所对应的实体类型(Discriminator), 使用 Table 标注无效
- 抽象类的直接子类支持多个类对应到多个表
- 同一个表的不同类型有相同属性时,默认将创建多列; 如果属性类型相同,可以通过 FluentAPI 定义属性对应到同一列;
- 考虑如下情形, 非抽象类 (Blog) --- 抽象类 ( AbstractBlog ) --- 非抽象类 (RssBlog ) 会如何创建表?
** 还是只会创建 Blog 表并带有 鉴别器列
-
序列
注意,数据库不同时,从序列取值的语法也不同,
例如 ORACLE OrderNumbers.NEXTVAL
modelBuilder.HasSequence<int>("OrderNumbers", schema: "shared")
.StartsAt(1000)
.IncrementsBy(5);
modelBuilder.Entity<Order>()
.Property(o => o.OrderNo)
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers");
-
字段映射 / 非空构造函数的实体 ( 只读属性 ) / 服务注入到实体类的构造函数
- 非空构造函数的实体 ( 只读属性 ), 实体类不包含空构造函数,只包含非空构造函数
- 属性只读 ( 由构造函数写入 ) , 保证属性会被正确写入
- JSON反序列化时可能出错
-
值转换器 和 值比较器
- 值转换器允许在读取或写入数据库时转换属性值。
- 值比较器
- 将一个表拆分为多个实体
- 需要定义实体映射到同一个表
- 需要定义实体间的关系 ( 一般是 一对一 的关系 )
- 实体有相同属性对应到同一列时需要定义属性到列的映射
- 有并发令牌( 指定义的 Timestamp 列 或者 IsRowVersion ) 时, 并发令牌属性必须定义在所有实体中, 以及定义并发令牌属性所对应的列的映射;
- 当更新时, 保存某一个实体, 将查询新的令牌, 此令牌将更新到 DbContext 下相同Id实体相同表的所有其他令牌属性(即使是隐藏属性也会更新)
modelBuilder.Entity<Order>()Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
modelBuilder.Entity<DetailedOrder>().Property(o => o.Version).IsRowVersion().HasColumnName("Version");
......
var order = context.Orders.First();
var detailOrder = context.DetailedOrders.First();
detailOrder.BillingAddress = "广州市";
context.SaveChanges(); <== 这儿会查询新的 令牌, 并更新到 order 中
order.Status = OrderStatus.Shipped;
context.SaveChanges();
- 从属实体类型
https://docs.microsoft.com/zh-cn/ef/core/modeling/owned-entities- 从属实体类型表示某一个类型的实例被其他类型的实例所拥有
- FluentAPI: OwnsOne, 从属实体类型可以单独映射到另外一个表
public class DetailedOrder
{
public int Id { get; set; }
//通过 FluentAPI表示此为从属类型,并且映射到单独表
//查询时总是会把 OrderDetails 表中的数据查询出来
public OrderDetails OrderDetails { get; set; } ,
}
public class OrderDetails
{
// 不注释将创建 OrderId 的外键字段,
// 注释后将创建 DetailOrderId 的外键字段
// public DetailedOrder Order { get; set; }
// 嵌套的从属实体类型, 默认下创建 BillingAddress_Street 和 BillingAddress_City 2个字段
// 通过 FluentAPI 将上面2个字段变为了 BillStreet 和 BillCity
public StreetAddress BillingAddress { get; set; }
// 嵌套的从属实体类型, 将创建 ShippingAddress_Street 和 ShippingAddress_City 2个字段
public StreetAddress ShippingAddress { get; set; }
}
[Owned]
public class StreetAddress
{
public string Street { get; set; }
public string City { get; set; }
}
....
modelBuilder.Entity<DetailedOrder>().OwnsOne(p => p.OrderDetails, od =>
{
od.ToTable("OrderDetails");
od.OwnsOne(x => x.BillingAddress, bi =>
{
bi.Property(y => y.Street).HasColumnName("BillStreet");
bi.Property(y => y.City).HasColumnName("BillCity");
});
//下面方法抛出异常
//od.Property(x => x.ShippingAddress.City).HasColumnName("ShipAddr_City");
});
- [Owned] 标注, 表示某一个类是从属实体类型
- FluentAPI: 1对多的从属实体类型
public class Distributor
{
public int Id { get; set; }
// 将创建 Distributor_ShippingCenters 表, 将在查询 Distributor 中自动查询词表
// Distributor_ShippingCenters 会自动创建 DistributorId (外键关联), Id ( 表主键),
// Street, City ( StreetAddress 实体中的属性列 )
public ICollection<StreetAddress> ShippingCenters { get; set; }
}
- EFCore 能够检测到从属实体实例的属性的变更;
context.DetailedOrders.First().OrderDetails.BillingAddress.Street = "怡雅街";
context.Distributors.First().ShippingCenters.First().Street = "ABC";
- 无主键实体类型
- [KeyLess] 或者
modelBuilder.Entity<BlogPostsCount>().HasNoKey();
- EFCore 不会追踪实体的变更, 只能作为只读数据源
- 可以通过 ToTable 映射到表 或者 ToView 映射视图
- 可以变更映射列
- [KeyLess] 或者
{
eb.HasNoKey();
eb.ToView("View_BlogPostCounts");
eb.Property(v => v.BlogName).HasColumnName("Name");
});
- 根据 DbContext 属性参数化实体
- OnModelCreating 中生成的模型可以使用上下文中的属性来更改模型的生成方式
EF 会构建模型并只运行一次 OnModelCreating,并且出于性能原因缓存结果 - 改变 DbContext 类的缓存健,来使得 OnModelCreating 对于不同缓存健, 各个执行一次
- 实现 IModelCacheKeyFactory 接口 以及 注册新的 IModelCacheKeyFactory 的类
// 实现 IModelCacheKeyFactory 接口的类
public class DynamicModelCacheKeyFactory : IModelCacheKeyFactory
{
//根据 DbContext 获取缓存的 Key
//当 DbContext 是 DynamicContext 类型实例时,
//返回 带有 (context.Type, UseIntProperty) 的一个结构作为 Key
//这样, 当 UseIntProperty 不同时,返回的 Key 也不同;
public object Create(DbContext context)
=> context is DynamicContext dynamicContext
? (context.GetType(), dynamicContext.UseIntProperty)
: (object)context.GetType();
}
DynamicContext .Cs
// optionsBuilder 中注册 DynamicModelCacheKeyFactory 类型为 IModelCacheKeyFactory 接口的提供者
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactory>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (UseIntProperty) // 这儿,在 ( DynamicContext , true) 作为 Key 时会执行一次
{
modelBuilder.Entity<ConfigurableEntity>().Ignore(e => e.StringProperty);
}
else // 这儿,在 ( DynamicContext , false) 作为 Key 时会执行一次
{
modelBuilder.Entity<ConfigurableEntity>().Ignore(e => e.IntProperty);
}
}
- 空间库 NetTopologySuite , 不同数据库提供者有不同的包, 例如 Microsoft.EntityFrameworkCore.SqlServer 需要包 Microsoft.entityframeworkcore. NetTopologySuite
- EF Core 使用模型中的 NTS 类型启用映射到数据库中的空间数据类型, 需要代码启用映射
optionsBuilder.UseSqlServer( @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters", x => x.UseNetTopologySuite());
- 支持的空间类型 ( NetTopologySuite.Geometries 命名空间 )
* Geometry, Point, LineString, Polygon, GeometryCollection, MultiPoint, MultiLineString, MultiPolygon - 创建空间对象 ( 建议使用 工厂方法 )
下面的 srid 指定 4326, 指的是 WGS 84,是 GPS 和其他地理系统中使用的标准。
x 表示经度, y 表示纬度, 客户端计算的值(距离,长度等)将是度数;
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var currentLocation = geometryFactory.CreatePoint(-122.121512, 47.6739882);
- Linq查询, 可用作数据库函数的 NTS 方法和属性将转换为 SQL
var nearestCity = db.Cities .OrderBy(c => c.Location.Distance(currentLocation)) .FirstOrDefault();
- 默认情况下,空间属性映射到 SQL Server 中的 geography 列。建议通过 Column 标注配置为 geometry 列类型, 限制较少;
* geography 限制, 外环必须逆时针, 内环必须顺时针(如果有)
网友评论