为什么使用反射查询 DBSet 会生成包含所有列的查询?
Why does using reflection to query a DBSet generate a query that includes all columns?
当我使用匿名类型从一个实体 select 一个 属性 时,生成的 SQL 是有效的并且只包括在匿名类型中查询的列。
例如:
dbContext.ExampleEntities.Select(obj => new {
obj.Id
});
生成以下内容SQL:
SELECT
[Extent1].[ExampleEntity_Id] AS [ExampleEntity_Id]
FROM [dbo].[ExampleEntity] AS [Extent1]
GO
但是,如果我使用反射在多个 DBSet 中执行类似的操作,生成的 SQL 包括未作为匿名类型的一部分查询的实体的属性。
在我的用例中,我试图查询位于基 class 中的属性,我的所有实体 class 都源自该基 class。
例如:
public class EntityBase {
public virtual int Id { get; set; }
}
我正在查询包含从 EntityBase
派生的实体的 DBSet,如下所示:
dbContext.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType)
.Where(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.Where(p => p.PropertyType.GetGenericArguments().First().IsSubclassOf(typeof(EntityBase)))
.SelectMany(p => (IEnumerable<EntityBase>)p.GetValue(dbContext, null))
.Select(obj => new { obj.Id });
但是,为从 EntityBase
派生的每个实体生成一个单独的查询,并且 SQL 包括匿名类型中未使用的列:
SELECT
[Extent1].[DerivedEntity_Id] AS [DerivedEntity_Id],
[Extent1].[DerivedEntityPropertyOne] AS [DerivedEntityPropertyOne],
[Extent1].[DerivedEntityPropertyTwo] AS [DerivedEntityPropertyTwo],
...
FROM [dbo].[DerivedEntity] AS [Extent1]
GO
如何避免将包含从 EntityBase
派生的实体的每个 DBSet 的每一列都拉入内存?
编辑
我尝试了下面@Pawel 的建议并将转换更改为 IQueryable,但这没有任何效果。
我能够通过将查询转换为以下格式来实现我所需要的:
dbContext.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType)
.Where(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.Select(p => p.PropertyType.GetGenericArguments().First())
.Where(t => t.IsSubclassOf(typeof(EntityBase)))
.Select(t => ((IQueryable<EntityBase>)dbContext.Set(t)).Select(obj => obj.Id))
.SelectMany(obj => obj)
.Dump();
它的可读性稍差,因为最后一个 Where
之后的 Select
必须嵌套,但它有效。
我无法让 linq 提供程序生成 SQL 语句,该语句是所有 DBSet
的单一联合,但这足以满足我的目的。
当我使用匿名类型从一个实体 select 一个 属性 时,生成的 SQL 是有效的并且只包括在匿名类型中查询的列。
例如:
dbContext.ExampleEntities.Select(obj => new {
obj.Id
});
生成以下内容SQL:
SELECT
[Extent1].[ExampleEntity_Id] AS [ExampleEntity_Id]
FROM [dbo].[ExampleEntity] AS [Extent1]
GO
但是,如果我使用反射在多个 DBSet 中执行类似的操作,生成的 SQL 包括未作为匿名类型的一部分查询的实体的属性。
在我的用例中,我试图查询位于基 class 中的属性,我的所有实体 class 都源自该基 class。
例如:
public class EntityBase {
public virtual int Id { get; set; }
}
我正在查询包含从 EntityBase
派生的实体的 DBSet,如下所示:
dbContext.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType)
.Where(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.Where(p => p.PropertyType.GetGenericArguments().First().IsSubclassOf(typeof(EntityBase)))
.SelectMany(p => (IEnumerable<EntityBase>)p.GetValue(dbContext, null))
.Select(obj => new { obj.Id });
但是,为从 EntityBase
派生的每个实体生成一个单独的查询,并且 SQL 包括匿名类型中未使用的列:
SELECT
[Extent1].[DerivedEntity_Id] AS [DerivedEntity_Id],
[Extent1].[DerivedEntityPropertyOne] AS [DerivedEntityPropertyOne],
[Extent1].[DerivedEntityPropertyTwo] AS [DerivedEntityPropertyTwo],
...
FROM [dbo].[DerivedEntity] AS [Extent1]
GO
如何避免将包含从 EntityBase
派生的实体的每个 DBSet 的每一列都拉入内存?
编辑
我尝试了下面@Pawel 的建议并将转换更改为 IQueryable,但这没有任何效果。
我能够通过将查询转换为以下格式来实现我所需要的:
dbContext.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType)
.Where(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.Select(p => p.PropertyType.GetGenericArguments().First())
.Where(t => t.IsSubclassOf(typeof(EntityBase)))
.Select(t => ((IQueryable<EntityBase>)dbContext.Set(t)).Select(obj => obj.Id))
.SelectMany(obj => obj)
.Dump();
它的可读性稍差,因为最后一个 Where
之后的 Select
必须嵌套,但它有效。
我无法让 linq 提供程序生成 SQL 语句,该语句是所有 DBSet
的单一联合,但这足以满足我的目的。