美文网首页
2020-03-28 EFCore 的教程 -- 提纲2

2020-03-28 EFCore 的教程 -- 提纲2

作者: daiwei_9b9c | 来源:发表于2020-05-17 20:19 被阅读0次
    1. EFCore入门

      • 配置数据库连接字符串
      • EFCore 如何自动获知数据库主键
      • 将项目的变动更新到数据库
      • 安装 EFCore 的数据库驱动
    2. 索引

      • 索引 ( HasIndex ) / 唯一索引 ( IsUnique ) / 包含附加字段的索引 ( IncludeProperties) / 指定筛选条件的索引 ( HasFilter )
    3. 表之间的关系

      • 主体实体 / 主体健 / 相关实体 / 外键
      • 导航属性 / 集合导航属性 / 引用导航属性 / 反向导航属性
      • 关系 / 自引用关系
      • 不使用 FluentApi 定义时 -- 完全定义关系 / 单个导航属性
      • FluentApi ( HasOne / WithMany / WithOne / HasMany )
      • 什么情况下需要使用 FluentAPI
      • 影子外键
    4. InverseProperty 标注的用途

    5. 阴影属性

      • 定义阴影字段
      • 访问阴影字段
    6. 继承

      • 非抽象类以及其子类只支持对应到单个表, 使用鉴别器列区分行所对应的实体类型(Discriminator), 使用 Table 标注无效
      • 抽象类的直接子类支持多个类对应到多个表
      • 同一个表的不同类型有相同属性时,默认将创建多列; 如果属性类型相同,可以通过 FluentAPI 定义属性对应到同一列;
      • 考虑如下情形, 非抽象类 (Blog) --- 抽象类 ( AbstractBlog ) --- 非抽象类 (RssBlog ) 会如何创建表?
        ** 还是只会创建 Blog 表并带有 鉴别器列
    7. 序列

    注意,数据库不同时,从序列取值的语法也不同,
    例如 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");
    
    1. 字段映射 / 非空构造函数的实体 ( 只读属性 ) / 服务注入到实体类的构造函数

    • 非空构造函数的实体 ( 只读属性 ), 实体类不包含空构造函数,只包含非空构造函数
    • 属性只读 ( 由构造函数写入 ) , 保证属性会被正确写入
    • JSON反序列化时可能出错
    1. 值转换器 和 值比较器

    • 值转换器允许在读取或写入数据库时转换属性值。
    • 值比较器
    1. 表拆分
      https://docs.microsoft.com/zh-cn/ef/core/modeling/table-splitting]
    • 将一个表拆分为多个实体
    • 需要定义实体映射到同一个表
    • 需要定义实体间的关系 ( 一般是 一对一 的关系 )
    • 实体有相同属性对应到同一列时需要定义属性到列的映射
    • 有并发令牌( 指定义的 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();
    
    1. 从属实体类型
      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";
    
    1. 无主键实体类型
      • [KeyLess] 或者 modelBuilder.Entity<BlogPostsCount>().HasNoKey();
      • EFCore 不会追踪实体的变更, 只能作为只读数据源
      • 可以通过 ToTable 映射到表 或者 ToView 映射视图
      • 可以变更映射列
            {
                eb.HasNoKey();
                eb.ToView("View_BlogPostCounts");
                eb.Property(v => v.BlogName).HasColumnName("Name");
            });
    
    1. 根据 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);
        }
    }
    
    1. 空间数据
      https://docs.microsoft.com/zh-cn/ef/core/modeling/spatial
    • 空间库 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 限制, 外环必须逆时针, 内环必须顺时针(如果有)

    相关文章

      网友评论

          本文标题:2020-03-28 EFCore 的教程 -- 提纲2

          本文链接:https://www.haomeiwen.com/subject/vnssohtx.html