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?
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
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.
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>());
Related
I want to make a projection as a performance wise but the select part returns an anonymous type and I can't to make required mapping.
var jobDegreesQuery = _context.JOBDEGREEs.AsQueryable().Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME });
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
.ToList()
.Select(Mapper.Map<JOBDEGREE, JobDegreeDTO>); //The error
The type arguments for method 'Enumerable.Select(IEnumerable, Func)' cannot be
inferred from the usage. Try specifying the type arguments explicitly.
How can I do the projection and map to DTO Successfully ?
As I understand you want to map JOBDEGREEs to JobDegreeDTO. You are first selecting it as anonymous type, so I think AutoMapper can not map because you are giving anon. type.
Change your code as below it will perform better:
IQueryable<JOBDEGREEs> jobDegreesQuery = _context.JOBDEGREEs; // it is already queryable
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
//.Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME }) // do you need this?
.Select(d => Mapper.Map<JOBDEGREE, JobDegreeDTO>(d)); // here you can give any expression
.ToList()
What is the result of your ToList()? It is a List of objects of some anonymous class, that contains data extracted from your sequence of JobDegrees
Whenever you want to use Enumerable.Select on a sequence of objects, you'll first have to name an identifier that represents one element of your sequence. This identifier is the part before the =>. After the => you'll write the code to return one object using this input identifier.
This is a difficult way to say something like:
IEnumerable<Person> myPersons = ...
var firstNames = myPersns.Select(person => person.FirstName);
Here the person before the => represents one item of your collection of Persons. Hence person seems a proper name for this identifier.
If you want you can use any identifier to identify a person, although not all identifiers will improve readability:
var firstNames = myPersns.Select(x => x.FirstName);
When using LINQ and entity framework it is good practice to identify collections with plural nouns and elements of collections with singular nouns.
After the => you write some code that uses this input person to return exactly one object. In this example the FirstName of the person.
Back to your question
The result of your ToList is a sequence of objects with a DegreeCode and a DegreeName.
If you want to convert every object in your sequence into one other object (this is called projection), you'll have to identify one object of your sequence before the '=>'.
For example
...ToList()
.Select(extractedDegreeData => ...)
Here, every extractedDegreeData corresponds with one element of your list.
Now what do you want to do with one such extractedDegreeData? You want to return the return value of Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData).
Therefore your code should be like:
...ToList()
.Select(extractedDegreeData => Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData));
Advice:
While constructing your LINQ query, don't use functions like ToList, or any other functions that does not return IEnumerable<TResult>, it is a waste of processing power. What if after your Select you would have put Take(2)? What a waste to create the complete list of 1000 elements if you only wanted the first two!
Therefore functions like ToList, FirstOrDefault, Max, Count should always be the last in your linq query.
Finally: dbContext.JobDegrees is a DbSet<JobDegree>, which implements IQueryable<JobDegree>, hence there is no need to use AsQueryable.
I am having trouble generating an IQuerable result from a group by clause in linq to Entities for range of values.
IQueryable<Model.MyEntity> query = MyContext.GetDbSet()
IQueryable<MyObject> query2 = null;
query2 = query.Select(x => new MyObject()
{
GroupingColumn = SqlFunctions.StringConvert(arrayMin.AsQueryable().FirstOrDefault(s => x.Amount > s)) +
"-" + SqlFunctions.StringConvert(arrayMax.AsQueryable().FirstOrDefault(s => x.Amount < s)) ,
CountOfAmountRange = 1,
SumOfAmount = (decimal)x.Amount,
});
query2 = query2.GroupBy(cm => new {cm.GroupingColumn }).Select(y => new MyObject()
{
GroupingColumn =y.Key.GroupingColumn ,
CountOfAmountRange = y.Count(),
SumOfAmount = (decimal)y.Sum(p => p.SumOfAmount)
});
A bit of context:
I am working on a highly structured application which has a layer responsible for generating queries which will be later applied to retrieve data from context in another layer. I have successfully used this to generate many reports but this specific one throws the error below.
The type 'MyObject' 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.
The arrayMax and arrayMin are arrays of decimal values containing the maximum and minimum values respectively for comparison and generating of string values for the GroupingColumn.
I have seen a couple of questions relating this error on StackOverflow but none of the answers seem to show me the solution to my problem.
For future readers, this SO duplicate (added one year later) was key to solving my problems:
The type appear in two structurally incompatible initializations within a single LINQ to Entities query
When you look at it, the error message is abundantly clear. Don't mess up the initialization order if you are instantiating an object more than once in the same Linq expression. For me, I instantiated a type conditionally, but with different initialization orders.
I am having issues with a single query, I am attempting to take a list of objects (opList) and extract 2 UID's per object to create a distinct list for another query.
DataContext.Where(x => opList.Select(y => y.UIDFirst)
.Union(opList.Select(o => o.UIDSecond)).ToList()
.Contains(x.uid)).ToList();
while each of the selects work by themselves, and the union works to join the lists to a unique list (tested in imediates window) an exception is thrown when processes the statement as a whole.
any ideas on what I did wrong;
{"Unable to create a constant value of type 'DataContext.Class.Operation'. Only primitive types or enumeration types are supported in this context."}
This should work:
var uids = opList.Select(o => o.UIDFirst)
.Concat(opList.Select(o => o.UIDSecond)
.ToList();
var result = DataContext.Where(dc => uids.Contains(x.uid)).ToList();
You have to call your statement on a certain DbSet contained in the DataContext, not on the DataContext directly.
DatContext.SomeDbSet.Where(x => /* ... */ );
This question is chiefly about LINQ and possibly covariance.
Two of my Entities implement the IDatedItem interface. I'd like to union, then sort these, for enumerating as a single list. I must retain entity-specific properties at enumeration-time.
To clarify by example, one approach I tried was:
Context.Table1.Cast<IDatedItem>().
Union(Context.Table2.Cast<IDatedItem>()).
SortBy(i => i.Date).
ForEach(u => CustomRenderSelector(u, u is Table1));
In trying to do this various ways, I've run into various errors.
LINQ to Entities only supports casting EDM primitive or enumeration types.
Unable to process the type '.IDatedItem[]', no known mapping to the value layer
Unable to create a constant value of type 'IDatedItem'. Only primitive types
etc.
The bigger picture:
The IDatedItem interface shown here is a simplification of the actual shared properties.
In practice, the tables are filtered before the union.
The entity-specific properties will be rendered, in order, in a web page.
In a parallel feature, they will be serialized to a JSON result hierarchy.
I'd like to be able to perform LINQ aggregate operations on the results as well.
This requires more space than a comment offers. On the other hand, this is not really an answer, because there is no satisfying answer, really.
For a Union to succeed, both collections must have the same type (or have intrinsic conversions to common types, that's what covariance is about).
So a first go at getting a correct Union could be:
Context.Table1.Select(t1 => new {
A = t1.PropA,
B = t1.PropB,
Date = t1.Date
})
.Union(
Context.Table1.Select(t2 => new {
A = t2.PropC,
B = t2.PropD,
Date = t2.Date
}))
.OrderBy(x => x.Date)
.ToList();
which projects both tables to the same anonymous type. Unfortunately, because of the anonymous type, you can't do .Cast<IDatedItem>().
Therefore, the only way to get a List<IDatedItem> is to define a type that implements IDatedItem and project both tables to that type:
Context.Table1.Select(t1 => new DateItem {
A = t1.PropA,
B = t1.PropB,
Date = t1.Date
})
.Union(
Context.Table1.Select(t2 => new DateItem {
A = t2.PropC,
B = t2.PropD,
Date = t2.Date
}))
.OrderBy(item => item.Date)
.AsEnumerable()
.Cast<IDatedItem>()
Which (I think) is quite elaborate. But as long as EF doesn't support casting to interfaces in linq queries it's the way to go.
By the way, contrary to what I said in my comment, the sorting will be done in SQL. And you can use subsequent aggregate functions on the result.
Here's the working code. My solution was to ensure all data was local, to keep LINQ-to-EF from trying to do all manner of things it knows it can't that were causing many unclear errors. Then a simple type declaration on the generic Union can take hold.
That means that, aside from the annoyances of LINQ-to-EF, the main issue here is really a duplicate of LINQ Union different types - dynamically casting to an interface? .
public virtual ActionResult Index() {
return View(StatusBoard().OrderBy(s => s.Status));
}
private IEnumerable<DefensiveSituationBoardMember> StatusBoard() {
DateTime now = DateTime.UtcNow;
DateTime historicalCutoff = now.AddDays(-1);
IEnumerable<Member> activeMembers = Context.Members.Where(n => !n.Disabled).ToList();
// IncomingAttack and Reinforcements both implement IDefensiveActivity
IEnumerable<IncomingAttack> harm = Context.IncomingAttacks.Where(n => n.IsOngoingThreat || n.ArrivalOn > historicalCutoff).ToList();
IEnumerable<Reinforcements> help = Context.Reinforcements.Where(n => !n.Returned.HasValue || n.Returned > historicalCutoff).ToList();
// Here's the relevant fix
IEnumerable<IDefensiveActivity> visibleActivity = help.Union<IDefensiveActivity>(harm);
return from member in activeMembers
join harmEntry in harm on member equals harmEntry.DestinationMember into harmGroup
join activityEntry in visibleActivity on member equals activityEntry.DestinationMember into activityGroup
select new DefensiveSituationBoardMember {
Member = member,
Posture = harmGroup.Max(i => (DefensivePostures?)i.Posture),
Activity = activityGroup.OrderBy(a => a.ArrivalOn)
};
}
I would think this is very basic stuff, but I'm just not getting it to work.
I try to get a list of objects using lambda expressions like this :
List<LocalizationGlobalText> list = _entities.LocalizationGlobalTexts.Where(l => l.Language.Id == _currentlanguage).ToList<LocalizationGlobalText>();
The list is fetched, but the foreign key objects are all null.
I also tried using LINQ to entities but this results in the same problem :
IEnumerable<LocalizationGlobalText> bla = (from lgt in _entities.LocalizationGlobalTexts
join lg in _entities.LocalizationGlobals on lgt.IdLocalizationGlobal equals lg.Id
where lgt.IdLanguage == _currentlanguage
select lgt);
By default, Entity Framework only brings in the collection that you specify, without any foreign objects. If you have lazy loading enabled, accessing the foreign properties will cause them to be lazily initialized. If not, you'll need to tell entity framework to eagerly load the properties you want with the first batch.
There are two ways to do this. The first is the "official" way, but I don't like it because it uses magic strings:
var list = _entities.LocalizationGlobalTexts.Include("ForeignProp")
.Where(l => l.Language.Id == _currentlanguage)
.ToList<LocalizationGlobalText>();
(Replace "ForeignProp" with the name of the property you want it to eagerly load)
The second way is to set up your selector so that it will be forced to pull this extra data in:
var list = _entities.LocalizationGlobalTexts
.Where(l => l.Language.Id == _currentlanguage)
.Select(l => new {l, l.ForeignProp})
.ToList();
foreach(var item in list)
{
Console.WriteLine(item.l.Name + item.ForeignProp.Title);
}
Since Entity Framework is smart enough to have made the appropriate connections, you could throw on one more selector and avoid using the anonymous type afterward:
var list = _entities.LocalizationGlobalTexts
.Where(l => l.Language.Id == _currentlanguage)
.Select(l => new {l, l.ForeignProp})
.AsEnumerable() // tells EF to load now. The rest is LINQ to Objects
.Select(i => i.l)
.ToList();
foreach(var localization in list)
{
Console.WriteLine(localization.Name + localization.ForeignProp.Title);
}