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)
};
}
Related
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 have a list of objects that I want to reload their data.
Like always, I have several options. I wanted just to select these items but encountered this "Additional information": Unable to create a constant value of type 'Item'. Only primitive types or enumeration types are supported in this context.
// (System.Collections.Generic.List<Item> selectedItems)
System.Collections.Generic.List<Item> items;
var q = from i in db.Items
where selectedItems.Any(s => s.Id == i.Id)
select i;
items = q.ToList()
the following yields the same, as expected...
var q = db.Items.Where(i => selectedItems.Any(si => i.Id == si.Id));
items = q.ToList();
I could have reattached each of the objects and call the reload, but then I would have(or not, but I don't know how) to run across the db lot of times to reload their Navigation Properties.
The only "fine" solution I've found until now is selecting the Id's of selectedItems and then running with it like follows:
int[] itemIds = selectedItems.Select(i => i.Id).ToArray();
var q = db.Items.Where(i => itemIds.Any(iId => i.Id == iId)); //Of course `Contains` could be used instead of `Any` here, since `itemIds` is a simple array of integers
items = q.ToList();
But is it a necessity or there is a more straight forward, neat or proper way to accomplish this?
But is it a necessity or there is a more straight forward, neat or proper way to accomplish this?
Not that I can think of. EF will try and turn your where clause into SQL (which is not as easy as you'd think). When it parses the expression and encounters a call to Any on a collection of non-primitive types, it does not know how to generically convert that list into a list of values to put in an into an in clause and gives you the error you quoted.
When the source collection is a collection of primitive or enumeration types, it can turn the source collection into a list of values and create an in clause. Contains does the same thing (it is also shorter is closer to the intent IMHO):
var q = db.Items.Where(i => itemIds.Contains(i.Id));
I keep getting the following error when I execute the query below. I'm not sure how else to put in my condition. I haven't been able to find the same issue with other questions that have posed the same error output.
Unable to create a constant value of type 'Project.Models.Bill'. Only primitive types or enumeration types are supported in this context.
The code:
var billResults = db.Database.SqlQuery<Bill>("exec [dbo].[sp_getBills]").AsQueryable();
var results = db.Bills.Select(
a => new
{
a.Id,
a.Col1,
a.Col2,
ErrorCount = (int) (billResults.Where(x => x.BillResultsId == a.Id).Count())
}).Where(a => a.Col1 == "Test123");
I'd appreciate any help - been stuck for the past few hours on this.
Thanks!
The error says that you cannot use a.Id in that expression, only constant values. a.Id depends on the Where clause of db.Bills query.
You can solve this in three ways:
1) Define a class (let's call it X) with properties Id, Col1, Col2 and ErrorCount. Remove ErrorCount from this query and instead of creating an anonymous object, create an object of type X (ErrorCount will not be set). Iterate over the collection and set ErrorCount.
2) Use CROSS APPLY using Entity Framework: read Entity Framework and CROSS/OUTER APPLY
It would be something like this:
from t1 in billResults
from t2 in db.Bills.Where(t2 => t2.Id== t1.BillResultsId )
select new { ... }
3) Consider creating a Stored Procedure and use a CROSS APPLY join to achieve what you need (read about CROSS APPLY here https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/)
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>());
I've built the following LINQ query
var activeMembers = from m in context.py3_membershipSet
join c in context.ContactSet on m.py3_Member.Id equals c.ContactId
where m.statuscode.Value == 1
orderby m.py3_name
select m;
But I've since seen an example formatted as such:
var contacts =
(
from c in xrm.ContactSet
join a in xrm.AccountSet on c.ParentCustomerId.Id equals a.Id
where a.Name == "Acme Pty Ltd"
select new
{
Name = c.FullName,
DOB = c.BirthDate,
Gender = (c.FormattedValues.Contains("gendercode") ? c.FormattedValues["gendercode"] : "Ambiguous")
}
);
(I realise this is a different set of data) What does the inclusion of the 'select new' actually do in this case?
What are the benefits of it over my example in the first code block?
I realise some might find this a tedious question, but I a want to learn LINQ and need to learn it fast. But I also don't want to run something -that I don't fully understand- on a clients live CRM
LINQ returns a collection of anonymous objects, either way. select new let's you define the layout of that object and which properties/property names are included in that anonymous object.
You can also use select new ClassName { } to return a list of instances of an entity class that you define.
As previous answers have noted both methods return an anonymous type. To fully answer your question though: "What are the benefits of the second statement over the first?"
The first statement returns all of the fields of m as-is. If you have 7 "columns" then activeMembers will contain all of them and whatever data they contain.
In the second statement you're projecting the results into a custom anonymous object that has only 3 fields. Not only that but you can perform logic on the "source fields" before storing them in the anonymous object. This gives you much of the flexibility of storing them in a container class without you actually having to define that class in code.
You could also do select new SomeClass { } which would project your results into a predefined class container.
The following pseudo-code may or may not be helpful in understanding the difference:
var myQuery = from p in someContext.someTable
group p by p.someField into g
select new MyContainer {
Field1 = g.Sum(a => a.field1)
Field2 = g.Max(a => a.field2)
};
myQuery in the above is now a collection of MyContainer. If I had omitted the class MyContainer then it would be a collection of an Anonymous Type containing the fields that I specified. Obviously, the difference here is that MyContainer has to be defined elsewhere in your code whereas the anonymous class is built/defined for you at compile time. In:
var myQuery = from p in someContext.someTable
select p;
myQuery is an anonymous class containing all of the fields and their values in someTable.
You are using the Queryable.Select<TSource, TResult>(IQueryable<TSource>, Expression<Func<TSource, TResult>>) method where ContactSet is the TSource and the of anonymous object return type is the TResult. Your code could also be written as
...Select(r => new {
Name = c.FullName,
DOB = c.BirthDate,
Gender = (c.FormattedValues.Contains("gendercode") ? c.FormattedValues["gendercode"] : "Ambiguous")
})
where the select method is returning a collection of anonymous types.
Also, there is a bit more going on under the hood since it looks like you're querying the database. Your implementation of IQueryable looks at the Select method that you've written, and translates the expression you've provided into valid SQL so that it can retrieve all the necessary information for the anonymous object that you're returning. Notice that I said your implemenation of IQuerable translates the provided expression (not function) into sql. The Select extension method only accepts expressions, not functions because it can't translate functions to sql.