Merging two iqueryables that are projected to one entity from different tables

亡梦爱人 提交于 2020-01-04 01:12:07

问题


I have tried This answer, This one and this one to merge two iqueryables. But I always receive the following error:

The type 'Estudio' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.

I'm mapping from two different but similar Entity Framework Entities (EXAMEN and EXPLORACION) to my domain entity Estudio, with the following code.

IQueryable<Estudio> listExamen = context.Set<EXAMEN>().Project().To<Estudio>();
IQueryable<Estudio> listExploracion = context.Set<EXPLORACION>().Project().To<Estudio>();

var listCombined = listExamen.Concat(listExploracion);

Is there anyway of generate a IQueryable (not enumerable) with the merging of both list? If AsEnumerable() is used, then the following filters (Order, Take, etc) are executed on memory. So I need to merge the list but still be able to apply filter to the merged list wihtout execute the queries.

//This will force the next condition is executed on memory
    var listCombined = listExamen.AsEnumerable().Concat(listExploracion);

Is that possible?


回答1:


I would try to select your data into an anonymous type in your linq query, perform the union, and add your criteria.

var listExamen = context.Examen
    .Select(x => new { x.Prop1, x.Prop2, ... }); // Add properties

var listExploracion = context.Exploraction
    .Select(x => new { x.Prop1, x.Prop2, ... }); // Add identical properties

var listCombined = listExamen.Concat(listExploracion);

var whereAdded = listCombines
    .Where(x => x.Prop1 == someValue);
var result = whereAdded
    .Skip(skipCount)
    .Take(takeCount)
    .ToList();

Note: I have no idea if you can use Common Table Expressions (the SQL necessity for skip/take) in combination with a Union-query

Note: I've changed the methods used to create the expressions, since I do not know your methods (Project, To)

So I think the solution is not to cast to a specific type, but to an anonymous type, since that probably can be translated to SQL.

Warning: didn't test it




回答2:


My solution was to revise my mapping code. Instead of using individual property-based mappers, I had to project the entire entity at once, making sure that all of the properties were given in the same order.

So, instead of the ForMember syntax:

Mapper.CreateMap<Client, PersonResult>()
    .ForMember(p => p.Name, cfg => cfg.MapFrom(c => c.Person.FirstName + " " + c.Person.LastName))
    ...

I used the ProjectUsing syntax:

Mapper.CreateMap<Client, PersonResult>()
    .ProjectUsing(c => new PersonResult()
    {
        Name = c.Person.FirstName + " " + c.Person.LastName
        ...
    });

This must be because of the way AutoMapper constructs its projections.




回答3:


One way to work around this is to add dummy types:

class Estudio<T> : Estudio { }

And new mapping:

Mapper.CreateMap<Estudio , Estudio>();
Mapper.CreateMap<EXAMEN , Estudio<EXAMEN>>();
Mapper.CreateMap<EXPLORACION, Estudio<EXPLORACION>>();

One caveat is that all fields in Estudio need some value in mapping. You can't use ignore. Returning 0 or "" is fine. Now we can do:

var a = context.Set<EXAMEN>().ProjectTo<Estudio<EXAMEN>>();
var b = context.Set<EXPLORACION>().ProjectTo<Estudio<EXPLORACION>>();

return a.ProjectTo<Estudio>().Concat(b.ProjectTo<Estudio>());


来源:https://stackoverflow.com/questions/22810555/merging-two-iqueryables-that-are-projected-to-one-entity-from-different-tables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!