EF Core 2.0/2.1 - 如何高效处理大型、不常访问的列?
EF Core 2.0/2.1 - How to efficiently handle large, infrequently accessed columns?
我有一个table如下:
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。
Is there any way I can mark LargeBlob as Lazily Loaded so to avoid
huge wasted data transfers? Alternatively, are there other ways of
achieving the same outcome?
我尝试将 1->[0|1] 关系分成 2 个 table,如下所示:
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) 的各种组合,但我无法实现我想要实现的目标。回顾一下,那就是:
- 仅当 LargeBlob 属性 实际取消引用时才加载 Blob。
- 如果 entity.LargeBlob 属性 设置为新的 LargeBlob,(现在 "orphaned" )旧的 LargeBlob 将从数据库中删除。
- 如果实体被删除,相关的大 blob 也会被删除。
快速更新回复:版本 &c
注意:我使用的是 VS 2017 15.6.2、.net core 2.0 和 EF core 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" />
I tried splitting into 2 tables with a 1->[0|1] relationship as follows
但是通过将 FK 放在 Entity
中,您实际上做了相反的事情 - [0|1]->1 关系。
要获得所需的关系,FK 必须位于 LargeBlog
。可以是单独的属性(列),但最合适的是使用Id
属性作为PK和FK(所谓的共享PK关联).您可以使用以下流畅的配置来完成它:
modelBuilder.Entity<Entity>()
.HasOne(e => e.LargeBlob)
.WithOne()
.HasForeignKey<LargeBlob>(e => e.Id);
一旦你这样做了,因为这样做的全部目的是获得单独的可控(急切的、显式的或惰性的,如果可用)加载行为,可以看出,单独的 table 并不是真正需要的- 包含 blob 数据的 "entity" 可以使用 table splitting 嵌入到同一个 table 中,只需将以下内容添加到上述配置即可实现:
modelBuilder.Entity<Entity>().ToTable("MyTable");
modelBuilder.Entity<LargeBlob>().ToTable("MyTable");
请注意,虽然最合乎逻辑的选择似乎是自有类型,但不幸的是,当前自有类型总是被加载(类似于 EF6 复杂类型),因此它们不能用于实现可控的加载行为。
您应该只 select 节省带宽所需的列:
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();
我有一个table如下:
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。
Is there any way I can mark LargeBlob as Lazily Loaded so to avoid huge wasted data transfers? Alternatively, are there other ways of achieving the same outcome?
我尝试将 1->[0|1] 关系分成 2 个 table,如下所示:
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) 的各种组合,但我无法实现我想要实现的目标。回顾一下,那就是:
- 仅当 LargeBlob 属性 实际取消引用时才加载 Blob。
- 如果 entity.LargeBlob 属性 设置为新的 LargeBlob,(现在 "orphaned" )旧的 LargeBlob 将从数据库中删除。
- 如果实体被删除,相关的大 blob 也会被删除。
快速更新回复:版本 &c
注意:我使用的是 VS 2017 15.6.2、.net core 2.0 和 EF core 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" />
I tried splitting into 2 tables with a 1->[0|1] relationship as follows
但是通过将 FK 放在 Entity
中,您实际上做了相反的事情 - [0|1]->1 关系。
要获得所需的关系,FK 必须位于 LargeBlog
。可以是单独的属性(列),但最合适的是使用Id
属性作为PK和FK(所谓的共享PK关联).您可以使用以下流畅的配置来完成它:
modelBuilder.Entity<Entity>()
.HasOne(e => e.LargeBlob)
.WithOne()
.HasForeignKey<LargeBlob>(e => e.Id);
一旦你这样做了,因为这样做的全部目的是获得单独的可控(急切的、显式的或惰性的,如果可用)加载行为,可以看出,单独的 table 并不是真正需要的- 包含 blob 数据的 "entity" 可以使用 table splitting 嵌入到同一个 table 中,只需将以下内容添加到上述配置即可实现:
modelBuilder.Entity<Entity>().ToTable("MyTable");
modelBuilder.Entity<LargeBlob>().ToTable("MyTable");
请注意,虽然最合乎逻辑的选择似乎是自有类型,但不幸的是,当前自有类型总是被加载(类似于 EF6 复杂类型),因此它们不能用于实现可控的加载行为。
您应该只 select 节省带宽所需的列:
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();