EF Core 2.0 / 2.1 - 如何有效处理大量不经常访问的列?

时间:2018-03-24 22:05:33

标签: entity-framework entity-framework-core ef-core-2.0

我有一张表格如下:

CREATE TABLE MyTable
(
  ID INT NOT NULL PRIMARY KEY,
  NAME VARCHAR(50) NOT NULL, 
  LARGEBLOB VARBINARY(MAX) NULL
)

将实体定义为:

public class Entity
{
  public int Id {get;set;}
  public string Name {get;set;}
  public virtual byte[] LargeBlob {get;set;}
}

99%的用例仅涉及显示ID和NAME。

1%的时间我需要LARGEBLOB。

  

有什么方法可以将LargeBlob标记为Lazily Loaded以避免   巨大的浪费数据传输?或者,还有其他方法   实现相同的结果?

我尝试使用1-> [0 | 1]关系拆分为2个表格,如下所示:

CREATE TABLE MyTable
(
  ID INT NOT NULL PRIMARY KEY,
  NAME VARCHAR(50) NOT NULL, 
  LARGEBLOBID INT NULL
)

CREATE TABLE MySubTable
(
  ID INT PRIMARY KEY,
  LARGEBLOB VARBINARY(MAX) NOT NULL
)

包含实体

public class Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual LargeBlob LargeBlob { get; set; }
}

public class LargeBlob
{
    public int Id { get; set; }
    public virtual byte[] Blob { get; set; }
}

就延迟加载而言确实有效,但我尝试了所有方式的反向关系/外键标签,HasOne,OwnsOne,OnDelete(Cascade)各种组合,但我无法实现我的目标想要实现。简而言之,那就是:

  1. 仅在实际取消反映LargeBlob属性时才加载Blob。
  2. 如果将entity.LargeBlob属性设置为新的LargeBlob,则会从数据库中删除(现在为“孤立”)旧的LargeBlob。
  3. 如果实体被删除,相关的大blob将被删除。
  4. 快速更新:版本& c

    注意:我正在使用VS 2017 15.6.2,.net core 2.0,使用EF核心2.1(至少可以获得一些延迟加载的可能性)。 Nuget包:

    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="2.1.0-preview1-final" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0-preview1-final" PrivateAssets="All" />
    

2 个答案:

答案 0 :(得分:3)

  

我尝试使用1-> [0 | 1]关系拆分为2个表格,如下所示

但是把FK放在Entity你实际上做了相反的 - [0 | 1] - &gt; 1关系。

要获得所需的关系,FK必须为LargeBlog。它可以是一个单独的属性(列),但最合适的是使用Id属性作为PK和FK(所谓的共享PK关联)。您可以使用以下流畅的配置来完成此操作:

modelBuilder.Entity<Entity>()
    .HasOne(e => e.LargeBlob)
    .WithOne()
    .HasForeignKey<LargeBlob>(e => e.Id);

一旦你这样做,因为这样做的全部目的是获得单独的可控制(在可用的时候是热切的,显式的或懒惰的)加载行为,可以看出实际上不需要单独的表 - 包含“实体” blob数据可以使用table splitting嵌入到同一个表中,只需在上面的配置中添加以下内容即可实现:

modelBuilder.Entity<Entity>().ToTable("MyTable");
modelBuilder.Entity<LargeBlob>().ToTable("MyTable");

请注意,虽然最合乎逻辑的选择似乎是拥有类型,但遗憾的是,当前拥有的类型总是被加载(类似于EF6复杂类型),因此它们不能用于实现可控的加载行为。

答案 1 :(得分:0)

您应该只选择需要节省带宽的列:

var entity = await dbContext.Entities
  .Where(...)
  .Select(e => new
  {
    Id = e.Id,
    Name = e.Name,
    LargeBlob = null,
  })
  .FirstOrDefaultAsync();

,每当您确实需要LargeBlob列时,请手动加载

entity.LargeBlob = await dbContext.Entities
  .Where(e => e.Id == entity.Id)
  .Select(e => e.LargeBlob)
  .SingleOrDefaultAsync();

您可以删除实体而无需加载整个实体,仅Id(如果实体上存在并发令牌,则可以)

var entity = new Entity { Id = removeEntityId };
dbContext.Entities.Remove(entity);
await dbContext.SaveChangesAsync();