ObjectCollection.Where(o => o.Related == EntityObject): "Unable to create a constant value" - entity-framework

Given an EntityObject, I'd like an object-oriented way find all related items as part of my data-source query.
The following produces the correct output, but brings all the rows over the wire to do it.
Parent. // EntityObject
Children. // EntityCollection
Where(o => o.Gender == 'm'). // IEnumerable (local!)
OrderBy(o => o.Age). // IOrderedEnumerable
Skip(pages * pageSize).Take(pageSize); // (Inefficient paging!)
I need to support a UI with this (filter using other criteria, sort, and paginate before returning results over the wire). I reordered to leverage Queryable:
Repository. // Repository
Children. // ObjectSet
Where(o => o.Parent == Parent && o.Gender == 'm'). // ObjectQuery, runtime error
OrderBy(o => o.Age). // IOrderedQueryable
Skip(pages * pageSize).Take(pageSize);
but this yields the error:
Unable to create a constant value of type 'DataModel.Parent'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Is there a natural, object-oriented way to query on this relation, using Linq to Entities? Or do I necessarily fall-back to SQL for this?
I thought for a moment that CreateSourceQuery was going to be the answer, but it can't be applied to an EntityObject.

I can't do a test for what I'm saying, but I think that you get the error because EF doesn't know how to translate o.Parent == Parent into a SQL statement. Try compare the Ids of the two parents..
o.Parent.Id == Parent.Id

Related

Subquery checking for next elements

I am currently working on a Database-Security-System, have to make sure that in the next two minutes of this database there is no other entry that equals to true. But somehow I always end up getting a System.NotSupportedException.
My Code
public IQueryable<AuthorizationAttempt> GetSuspiciousActivity(
DateTime from,
DateTime to)
{
return GetActivity(from, to).Where(e1 =>
e1.Result == false
&& !GetActivity(
e1.AttemptDate,
e1.AttemptDate.AddMinutes(2d)
).Any(e2 => e2.Result == true));
}
Error
System.NotSupportedException: "LINQ to Entities does not recognize the method 'System.DateTime AddMinutes(Double)' method, and this method cannot be translated into a store expression."
Thanks a lot
You can't use any helper methods inside your LINQ query. Use DbFunctions instead.

Why is EqualityComparer<string>.Default not working?

var dic = context.Treasure.Include("TreasureShare")
.Where(t => t.TreasureShare.IsShared && t.TreasureShare.EvaluationContent.Contains(keyword))
.ToDictionary(t => t.ProductUrl, t => t.ProductId, EqualityComparer<string>.Default);
I got an error:
An item with the same key has already been added.
So why the equalitycomparer not work, and how to use a equalitycomparer to get different records while querying to database.
Updated:
I know IEqualityComparer can only be executed locally, but I didn't get an error like:
LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[Panli.Service.Share.DataAccess.DbData.Treasure] Distinct[Treasure](System.Linq.IQueryable1[Panli.Service.Share.DataAccess.DbData.Treasure], System.Collections.Generic.IEqualityComparer`1[Panli.Service.Share.DataAccess.DbData.Treasure])' method, and this method cannot be translated into a store expression.
except I change the codes to below:
dic = context.Treasure.Include("TreasureShare")
.Where(t => t.TreasureShare.IsShared && t.TreasureShare.EvaluationContent.Contains(theme))
.Distinct(new TreasureEqualityComparer()).ToDictionary(t => t.ProductUrl, t => t.ProductId);
This is my TreasureEqualityComparer:
public class TreasureEqualityComparer : EqualityComparer<Treasure>
{
public override bool Equals(Treasure x, Treasure y)
{
return x.ProductUrl.ToLower() == y.ProductUrl.ToLower();
}
public override int GetHashCode(Treasure obj)
{
return obj.ProductUrl.ToLower().GetHashCode();
}
}
So why not throw an exception just like the Distinct() when I use ToDictionary(..) which has an IEqualityComparer param ? Anyone can explain this ?
So why not throw an exception?
The ToDictionary part is executed in memory. This is apparent when you investigate the SQL that is executed: nothing that shows any preparation for a conversion to Dictionary.
The query expression with Distinct on the other hand is translated into SQL as a whole (except it isn't because it fails). EF tries to let the database do the hard work of returning distinct values, but of course a comparer can't be translated into SQL, so this overload of Distinct() is not supported.
As for the duplicate key: apparently there are duplicate URL's (ignoring case). Maybe you should use group by.
Dictionary key has to be unique. In this case you are using ProductUrl as dictionary key and ProductId as value, unfortunately as the error indicated there are more then one records in Table having same ProductUrl. So you can't use it as a dictionary key.

Entity Framework - Union causes "Unable to create a constant value of type.."

To select all Schedulings which are active, I have the following code:
var allSchedulesOnALine = CurrentUser.Lines.SelectMany(o => o.Scheduling).Where(o => o.Active);
var allSchedulesUnscheduled = Entities.SchedulingSet
.Where(o => o.Line == null && o.Site.Id == CurrentUser.Site.Id &&
o.Factory == CurrentUser.Factory && o.Active);
IEnumerable<Scheduling> allSchedules = allSchedulesUnscheduled.Union(allSchedulesOnALine);
foreach(Scheduling schedule in allSchedules.OrderBy(o => o.Ordering))
{
//Do Stuff
}
(Factory is an int)
When I run this code, I get this cryptic error on the foreach line:
Unable to create a constant value of type 'System.Collections.Generic.IEnumerable`1'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Strangely enough, I can enumerate both allSchedulesOnALine and allSchedulesUnscheduled separately. Even stranger, if I reorder the union:
IEnumerable<Scheduling> allSchedules = allSchedulesOnALine.Union(allSchedulesUnscheduled);
It works fine!
Does anyone have any idea why this would happen? Am I missing something crucial, or is this a bug?
I should mention I am using Entity Framework 3.5. EF4 is not an option for us currently - it is beyond my control :\
You're calling two different methods with your "reordering".
You don't show the types of allSchedulesOnALine or allSchedulesUnscheduled, but I'm betting allSchedulesOnALine is of type IEnumerable<Schedule> and allSchedulesUnscheduled is of type IQueryable<Schedule>.
So when you call Queryable.Union, you're asking the EF to translate the expression into SQL. But the argument you pass is of type IEnumerable<Schedule>, and it can't translate that into a query.
On the other hand, when you call Enumerable.Union, you're asking LINQ to Objects to do the whole thing in memory, which works fine, albeit perhaps slower.
So the reason the behavior is different is that you're calling two completely different methods, which do different things, but happen to have the same name. No, it's not a bug.

Unable to create a constant value of type 'System.Object'. Only primitive types ('such as Int32, String, and Guid') are supported in this context

I'm using MVC and Entity Framework. I've created a class in my model folder with this code below. I keep getting the error message above with both queries below. I know there is a known issue on referencing non-scalar variables, but I'm not sure how to implement a workaround:
http://msdn.microsoft.com/en-us/library/bb896317.aspx#Y1442
private MovieLibraryDBEntities movieLibraryDBEntitiesContext;
public int getNumberOfEntriesReserved()
{
return (from m in movieLibraryDBEntitiesContext.Movies
where m.CheckedOut.Equals(1)
select m).Count();
//return movieLibraryDBEntitiesContext.Movies
// .Where(e => e.CheckedOut.Equals(1))
// .Select (e => e.Title).Count();
}
You cannot use m.CheckedOut.Equals(1) in linq-to-entities query. Use m.CheckedOut == 1 but CheckedOut must be integer.
This is an older question. I had the same problem when trying to filter a nullable column using the IQueryable interface. I solved the problem by first checking to see if the object had a value and then checking the value.
widgets = widgets.Where(x => x.ID.HasValue.Equals(true) && x.ID.Value.Equals(widgetID));
same issue using Any()
i had to change my where clause to search on primitive types, for me int
so this
where order.User == user
becomes this
where order.User.UserId == user.UserId
There is a blog post explaining the quirk.

EF builds EntityCollection, but I (think I) want IQueryable

I have an entity A with a simple navigation property B. For any given instance of A, we expect several related thousand instances of B.
There is no case where I call something like:
foreach(var x in A.B) { ... }
Instead, I'm only interested in doing aggregate operations such as
var statY = A.B.Where(o => o.Property == "Y");
var statZ = A.B.Where(o => o.CreateDate > DateTime.Now.AddDays(-1));
As far as I can tell, EF instantiates thousands of references to B and does these operations in memory. This is because navigation properties use EntityCollection. Instead, I'd like it to perform these queries at the SQL level if possible.
My current hunch is that Navigation Properties may not be the right way to go. I'm not attached to EF, so I am open to other approaches. But I'd be very interested to know the right way to do this under EF if possible.
(I'm using EF4.)
CreateSourceQuery seems to do the trick.
So my examples would now be:
var statY = A.B.CreateSourceQuery().Where(o => o.Property == "Y");
var statZ = A.B.CreateSourceQuery().Where(o => o.CreateDate > DateTime.Now.AddDays(-1));
There's one thing you should know. Members that derives from IQueryable<> are executed on the server, not in memory. Members which are derived from IEnumerable<> is executed in memory.
for example
var someEntities = db.SomeEntities; <-- returns an IQueryable<> object. no data fetched. SomeEntities table may contain thousands of rows, but we are not fetching it yet, we are just building a query.
someEntities = someEntities.Where(s => s.Id > 100 && s.Id < 200); <-- creates expression tree with where statement. The query is not executed yet and data is not fetched on the client. We just tell EF to perform a where filter when query will execute. This statement too returns an IQueryable<> object.
var entities = someEntities.AsEnumerable(); <-- here we tell EF to execute query. now entities will be fetched and any additional linq query will be performed in memory.
you can also fetch the data using foreach, calling ToArray() or ToList<>.
Hope you understand what I mean, and sorry for my english :)