将 IQueryable<X> 转换为 IQueryable<Y> 以获得更好的性能
Cast IQueryable<X> to IQueryable<Y> with better performance
我有 2 个不同的实体,Entity1
和 Entity2
,它们具有完全相同的属性。它们是从数据库的不同视图自动生成的,每个视图都有自己的实体类型。
我通过以下方式查询这些实体:
protected generatedRepository<Entity1Type> _myRepository1;
_myRepository1.GetQueryable();
protected generatedRepository<Entity2Type> _myRepository2;
_myRepository2.GetQueryable();
我正在使用 OData 创建一个 API 端点,我必须 return IQueryable<...>
才能让用户将 OData 过滤器应用于其请求
当我想 return 来自 Entity1
的实体时,我只需要写:
public IQueryable<Entity1Type> Get()
{
return _myRepository.GetQueryable();
}
并且可以从 /api/ControllerName?$ODataFilter=...
访问这个新端点
但是,我想return有条件地从_myRepository1
或_myRepository2
使用相同的端点
获取数据]
如果我使用相同的签名,Entity2Type
必须转换为 Entity1Type
才能 returned
我试过了
return _myRepository2.GetQueryable().Cast<Entity1Type>();
但是失败了:
Unable to cast the type 'MyEntities2' to type 'MyEntities1'. LINQ to Entities only supports casting EDM primitive or enumeration types.
我也试过了:
return _myRepository2.GetQueryable().ToDTO<Entity2, Entity1>();
它有效,但视图有超过 100 万行并且它加载所有行,这是不可接受的
ToDto<>
方法来自:
我也尝试关注@DavidG 评论:
Mapper.CreateMap<Entity2Type, Entity1Type>
return _myRepository2.GetQueryable().ProjectTo<Entity1>();
但失败并出现此错误:
The entity or complex type 'Entity1Type' cannot be constructed in a LINQ to Entities query."
如何只创建一个端点,return从 _myRepository1
或 _myRepository2
中以良好的性能获取可查询数据?
您可以尝试使用与您的 2 个实体相匹配的通用 DTO 并编写正确的投影。
return _myRepository2.GetQueryable().Select(e2 => 'write your projection from e2 to dto')
实体 1 也一样:
return _myRepository1.GetQueryable().Select(e1 => 'write your projection from e1 to dto')
还有关于 ToDTO
我终于找到了解决方案,但我不确定它是否是最优雅的:
我把我的 Mapper.Map<>
改成了这样:
public IQueryable<Entity2> Get(ODataQueryOptions opts)
{
Mapper.CreateMap<Entity2Type, Entity2Type>() //Yes, Entity2 to Entity2, no typo
.ForMember(d => d.Prop1, option => option.MapFrom(src => someCondition ? src.Prop1 : src.Prop2))
.ForMember(d => d.Prop3, option => option.MapFrom(src => someCondition ? src.Prop3 : src.Prop4))
.IgnoreAllNonExisting();
var query = opts.ApplyTo(_myRepository2.GetQueryable()) as IQueryable<Entity2>;
var results = Mapper.Map<IList<Entity2>>(query.ToList());
return results.AsQueryable();
}
它 returns 一个 IQueryable<EntityB>
,所以我的用户可以使用 OData 过滤器。由于 ApplyTo<>
(因此在执行请求之前),它们被应用,这避免了将整个 table 加载到内存中,但只加载 returns 获取的结果。
我有 2 个不同的实体,Entity1
和 Entity2
,它们具有完全相同的属性。它们是从数据库的不同视图自动生成的,每个视图都有自己的实体类型。
我通过以下方式查询这些实体:
protected generatedRepository<Entity1Type> _myRepository1;
_myRepository1.GetQueryable();
protected generatedRepository<Entity2Type> _myRepository2;
_myRepository2.GetQueryable();
我正在使用 OData 创建一个 API 端点,我必须 return IQueryable<...>
才能让用户将 OData 过滤器应用于其请求
当我想 return 来自 Entity1
的实体时,我只需要写:
public IQueryable<Entity1Type> Get()
{
return _myRepository.GetQueryable();
}
并且可以从 /api/ControllerName?$ODataFilter=...
访问这个新端点但是,我想return有条件地从_myRepository1
或_myRepository2
使用相同的端点
如果我使用相同的签名,Entity2Type
必须转换为 Entity1Type
才能 returned
我试过了
return _myRepository2.GetQueryable().Cast<Entity1Type>();
但是失败了:
Unable to cast the type 'MyEntities2' to type 'MyEntities1'. LINQ to Entities only supports casting EDM primitive or enumeration types.
我也试过了:
return _myRepository2.GetQueryable().ToDTO<Entity2, Entity1>();
它有效,但视图有超过 100 万行并且它加载所有行,这是不可接受的
ToDto<>
方法来自:
我也尝试关注@DavidG 评论:
Mapper.CreateMap<Entity2Type, Entity1Type>
return _myRepository2.GetQueryable().ProjectTo<Entity1>();
但失败并出现此错误:
The entity or complex type 'Entity1Type' cannot be constructed in a LINQ to Entities query."
如何只创建一个端点,return从 _myRepository1
或 _myRepository2
中以良好的性能获取可查询数据?
您可以尝试使用与您的 2 个实体相匹配的通用 DTO 并编写正确的投影。
return _myRepository2.GetQueryable().Select(e2 => 'write your projection from e2 to dto')
实体 1 也一样:
return _myRepository1.GetQueryable().Select(e1 => 'write your projection from e1 to dto')
还有关于 ToDTO
我终于找到了解决方案,但我不确定它是否是最优雅的:
我把我的 Mapper.Map<>
改成了这样:
public IQueryable<Entity2> Get(ODataQueryOptions opts)
{
Mapper.CreateMap<Entity2Type, Entity2Type>() //Yes, Entity2 to Entity2, no typo
.ForMember(d => d.Prop1, option => option.MapFrom(src => someCondition ? src.Prop1 : src.Prop2))
.ForMember(d => d.Prop3, option => option.MapFrom(src => someCondition ? src.Prop3 : src.Prop4))
.IgnoreAllNonExisting();
var query = opts.ApplyTo(_myRepository2.GetQueryable()) as IQueryable<Entity2>;
var results = Mapper.Map<IList<Entity2>>(query.ToList());
return results.AsQueryable();
}
它 returns 一个 IQueryable<EntityB>
,所以我的用户可以使用 OData 过滤器。由于 ApplyTo<>
(因此在执行请求之前),它们被应用,这避免了将整个 table 加载到内存中,但只加载 returns 获取的结果。