I have 2 different entities, Entity1
and Entity2
, with the very same properties. They are auto-generated from different views from DB and each view has its own Entity type.
I query these entities via :
protected generatedRepository<Entity1Type> _myRepository1;
_myRepository1.GetQueryable();
protected generatedRepository<Entity2Type> _myRepository2;
_myRepository2.GetQueryable();
I'm creating an API endpoint using OData, and I must return a IQueryable<...> to let the user apply OData filters to its request
When I want to return entities from Entity1
, I just have to write :
public IQueryable<Entity1Type> Get()
{
return _myRepository.GetQueryable();
}
and this new endpoint is accessible from /api/ControllerName?$ODataFilter=...
However, I'd like to return data conditionnaly from _myRepository1
or _myRepository2
using the very same endpoint
If I use the same signature, Entity2Type
must be casted to Entity1Type
to be returned
I tried
return _myRepository2.GetQueryable().Cast<Entity1Type>();
But it fails :
Unable to cast the type 'MyEntities2' to type 'MyEntities1'. LINQ to Entities only supports casting EDM primitive or enumeration types.
I also tried :
return _myRepository2.GetQueryable().ToDTO<Entity2, Entity1>();
It works, but the views have more than 1M rows and it loads all rows, which is not acceptable
The ToDto<>
method came from : https://stackoverflow.com/a/8819149
I also tried following @DavidG comment :
Mapper.CreateMap<Entity2Type, Entity1Type>
return _myRepository2.GetQueryable().ProjectTo<Entity1>();
But it fails with this error :
The entity or complex type 'Entity1Type' cannot be constructed in a LINQ to Entities query."
How can I create only one endpoint, returning queryable data from _myRepository1
or _myRepository2
with good performance ?
CodePudding user response:
You could try to use a common DTO that match your 2 entities and write the proper projection.
return _myRepository2.GetQueryable().Select(e2 => 'write your projection from e2 to dto')
same for you entity1 :
return _myRepository1.GetQueryable().Select(e1 => 'write your projection from e1 to dto')
also about the ToDTO<T1, T2) exposed in the other post. There is a "ToList()" inside that execute the query.
CodePudding user response:
I've finally found a solution, but I'm not sure it's the most elegant :
I've changed my Mapper.Map<>
like this :
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();
}
It returns a IQueryable<EntityB>
, so my user can use OData filters. They are applied thanks to ApplyTo<>
(so before the request is executed), which avoids to load the entire table in memory but only returns the fetched results.