Entity Framework passing array of integers to endpoint - c#

I am passing an array of integers to an API endpoint composed in a model such as:
public class SomeModel {
public IList<int> ints { get; set; }
}
My endpoint to hit is:
Get["/myobjects/{id:int}"] = _ =>
{
var ints = this.Bind<SomeModel>();
return myService.someMethod1((int)_.id, ints.Ints));
};
Then, when I want to query the object in the DB,
var count = context.ReadOnly<SomeModel>()
.Query(x => x.myId == id && // id is already defined
ints != null &&
ints.Contains(x.SomeModelId))
.Select(x => x.Id)
.Count();
I am using Entity Framework as an ORM.
Unable to create a null constant value of type 'System.Collections.Generic.IList`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral]]'.
Or I get:
Cannot compare elements of type 'System.Collections.Generic.IList`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral]'. Only primitive types, enumeration types and entity types are supported.
Only entity types, enumeration types or primitive types are supported in this context.
The exception states that it can't create a null constant value, but I am populating the array that is coming in - so I don't understand why it is throwing this error.
EDIT: The array of ints is not being bound and the list is null. Does anybody know how to correctly bind to the list?

Have in mind all that checks you are doing in the lamdba are inside the query this means EF most probably will try to evaluate them to SQL.
But what you are checking is a variable that is not a part of the query. When the case is this, you need to split the check for null outside the query.
var query = context.ReadOnly<SomeModel>().Query(x => x.myId == id);
if (ints != null) {
query = query.Where(x => ints.Contains(x.SomeModelId);
}
var count = query.Select(x => x.Id).Count();
If I understood well the business case, if ints is null, the whole query will evaluate to false, so you most probably will be satisfied to just checking if ints is null -> if true, the count is zero. Otherwise - query the database.
var count = 0;
if (ints != null)
{
count = context.ReadOnly<SomeModel>()
.Query(x => x.myId == id &&
ints.Contains(x.SomeModelId))
.Select(x => x.Id)
.Count();
}

Related

EF6: How create a predicate which checks if a property is contained in the property of a list of objects

I have the following case:
I have a function which I have to call:
class WorkobjectRepository...
public async Task<IList<WorkobjectView>> FindAllMatchingAsync(Expression<Func<WorkobjectView, bool>> whereExpression)
{
return await this.WorkobjectContext.WorkobjectViews.AsNoTracking().Where(whereExpression).ToListAsync();
}
The Class WorkobjectView has among other properties the following two properties:
1. StatusDetailedDisplayName
2. AssignedSubteamId
Now I want all WorkobjectViews where StatusDetailedDisplayName == StatusDetailedDisplay.activeOpen and AssignedSubteamId is contained in a list of objects UserAccountSubteam where UserAccountSubteam has the property Id.
The following code works successfully for me:
// List<UserAccountSubteam> caredTeams, which is given
String[] teams = new string[caredTeams.Count];
for (int i = 0; i < caredTeams.Count; i++)
{
teams[i] = caredTeams[i].Id;
}
return await this.WorkobjectRepository.FindAllMatchingAsync(w => w.StatusDetailedDisplayName == StatusDetailedDisplay.activeOpen && teams.Contains(w.AssignedSubteamId));
Now, I wanted to optimize the code using LINQ or using collection functions.
Option 1: The first one was using LINQ and Exists method:
var results = await this.WorkobjectRepository.FindAllMatchingAsync(w => w.StatusDetailedDisplayName == StatusDetailedDisplay.activeOpen && caredTeams.Exists(team => team.Id == w.AssignedSubteamId));
But this failed and I received the following error message:
LINQ to Entities does not recognize the method 'Boolean Exists(System.Predicate`1[Backend.DomainLayer.UserAccountSubteam])' method, and this method cannot be translated into a store expression.
Option 2: Then, I tried to use Any:
var results = await this.WorkobjectRepository.FindAllMatchingAsync(w => w.StatusDetailedDisplayName == StatusDetailedDisplay.activeOpen && caredTeams.Any(team => team.Id == w.AssignedSubteamId));
which failed, too:
Unable to create a constant value of type 'Backend.DomainLayer.UserAccountSubteam'. Only primitive types or enumeration types are supported in this context.
Option 3: Last, but not least I tried the following which failed too:
var results = await this.WorkobjectRepository.FindAllMatchingAsync(w => w.StatusDetailedDisplayName == StatusDetailedDisplay.activeOpen && caredTeams.Select(c => c.Id).Contains(w.AssignedSubteamId));
Error message:
Unable to create a constant value of type 'Backend.DomainLayer.UserAccountSubteam'. Only primitive types or enumeration types are supported in this context.
How is the correct way to achieve this? And please note that I am not able to change the basic function FindAllMatchingAsync.
First option with Contains is correct way but can be simplified.
var teams = caredTeams.Select(t => t.Id);
return await this.WorkobjectRepository
.FindAllMatchingAsync(w => w.StatusDetailedDisplayName == StatusDetailedDisplay.activeOpen
&& teams.Contains(w.AssignedSubteamId));

EF6 Linq - How to create an filter expression equivalent to "where(List == null || List.Contains(obj))"?

I'm trying to do the following (SelectedIdCollection is List and cb.Id is int) -
db.Items.Where(cb => (SelectedIdCollection == null || SelectedIdCollection.Contains(cb.Id)))
Basically, if SelectedIdCollection is null then return everything, if it is not null then filter by it.
But it throws the following error -
An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code. Cannot
compare elements of type
'System.Collections.Generic.IList`1[[System.Int32, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.
Only primitive types, enumeration types and entity types are
supported.
Is there any other way of writing this where condition?
Since SelectedIdCollection is a variable captured from outside your expression, you can deal with it being null before making the expression, and then treat it as non-null:
var getEverything = SelectedIdCollection==null;
var targetCollection = SelectedIdCollection ?? new int[0];
var res = db.Items.Where(cb => getEverything || targetCollection.Contains(cb.Id));
Now targetCollection is guaranteed to be non-null, and getEverything flag covers the condition that requires selecting everything from the database.
The exception is being thrown because you are comparing a runtime variable (SelectedIdCollection != null), and EF doesn't know how to translate that into SQL.
Can you do something like this instead?
var items = db.Items.AsQueryable();
if(SelectedIdCollection != null)
{
items = items.Where(cb => SelectedIdCollection.Contains(cb.Id));
}
if(date1 != null)
{
items = items.Where(cb => cb.Date1 == date1);
}
This will potentially be faster in SQL as well, because the query planner might choose different indexes if it doesn't need to read all the columns to filter.

linq not returning any data

I have two variables. first variable docList of type www.abc.com.docListResponse. It consists of an array of values with docID[0] = "12345", docID[1] = "34567" docID[2] = "32498"
The second variable is of custom type Documents[] docsColl with values docRefID[0] = "12345", docRefID[1] = "34567", DocRefID[2] = "67890"
The ling operator
var docs = docsColl.ToList().Where(i => i.docRefID== docList .FirstOrDefault().docID.ToString()).ToArray();
I'm expecting it to return two records but its returning 0.
If you need to compose a query using another existing collection, you can generally use the Any() method to accomplish this, which will return any records that match your specific constraint.
var docs = docsColl.Where(d => docList.Any(x => d.docID == x.docRefID));
One caveat with this is that if you are using complex types and your initial collection is not in memory (and is in something like a database), LINQ won't know how to properly handle this, so you typically need to either throw a ToList() call prior to your Where() statement :
var docs = docsColl.ToList()
.Where(d => docList.Any(x => d.docID == x.docRefID));
Or you'll need to ensure that the collection you are using to query through (in this case docList) only consists of primitive types like strings :
// Get your IDs to check against
var docRefIds = docList.Select(d => d.docRefID).ToArray();
var docs = docsColl.Where(d => docRefIds.Any(r => d.docID == r));

Linq error: The cast to value type 'Int32' failed because the materialized value is null

... But it's not null.
FYI - Many threads exist on this error, but none that I've seen using an anonymous type.
I'm Getting an odd InvalidOperationException in a Linq query.
message: "The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."
The confusing thing is that it's erroring when creating an anonymous type:
var workstepid = 484,449;
var wsData = ion.xWorkSteps
.Where(w => w.WorkStepId == workstepId)
.Select(w => new
{
w.WorkStepId,
w.ServiceId,
w.Service.TitleId,
w.Service.Title.OrderId,
w.Service.Title.AltTitleId
}).SingleOrDefault();
In LinqPad this particular query runs just fine and the workstepId used returns an integer value for each property of the anonymous type. So why a casting error when there is no null value for any property!?
FYI, the last property AltTitleId is a nullable int, and the other properties are ints.
Also, this code was written weeks ago and I didn't get this error until today. Is something funky w/ my EF?
Edit: SOLVED
We use soft-delete in our db's, setting deleted records to Acive=0.
It turns out that there is a property set in EF on the edmx tables filtering out inactive records (filter for Active=1). Naturally, this wouldn't effect Linqpad which gives me the expected result, but since this was an older record I was testing, the title record (Service.Title) had been marked Active=0 and was therefore returning null.
Thanks to everyone for the help.
var workstepid = 484,449;
var wsData = ion.xWorkSteps
.Where(w => w.WorkStepId == workstepId)
.Select(w => new
{
w.WorkStepId.Value,
w.ServiceId.Value,
w.Service.TitleId.Value,
w.Service.Title.OrderId.Value,
w.Service.Title.AltTitleId.Value
}).SingleOrDefault();
Hi you need to add .Value in your int data type to accept null value. hope this helps
If w.WorkStepId is nullable, try setting your variable as...
int? workstepid = 484449;
I suppose, the problem is in your SingleOrDefault().
I suppose that you get 0 records:
var workstepid = 484,449;
var len = ion.xWorkSteps
.Where(w => w.WorkStepId == workstepId)
.Length();
And then the anonymouse type must be replace by Default (this is what SingleOrDefault do), but there is no default for your anonymous type.
Try to change it to:
var wsData = ion.xWorkSteps
.Where(w => w.WorkStepId == workstepId)
.Select(w => new
{
w.WorkStepId,
w.ServiceId,
w.Service.TitleId,
w.Service.Title.OrderId,
w.Service.Title.AltTitleId
})
.Take(1)
.ToArray();
May be there are no Service/Title/Order id somewhere, but if it declared as int anonymous type expect int (not nullable), but from database it returns null.
Try to rewrite as :
var workstepid = 484,449;
var wsData = ion.xWorkSteps
.Where(w => w.WorkStepId == workstepId)
.Select(w => new
{
WorkStepId = (int?)w.WorkStepId,
ServiceId = (int?)w.ServiceId,
TitleId = (int?)w.Service.TitleId,
OrderId = (int?)w.Service.Title.OrderId,
w.Service.Title.AltTitleId
}).SingleOrDefault();

Comparing a nullable column throws "Unable to cast the type..." exception

My entity NewsItem has a nullable foreign key property: LibraryID of type int?.
My issue is when I query the property and compare it with any value except null, I get exceptions.
Initially my code was:
int? lid = ...
var results = context.NewsItems
.Where(n => n.LibraryID == lid);
but it gives me no results at all, no matter what lid is.
So, I tried:
var results = context.NewsItems
.Where(n => n.LibraryID.Equals(lid));
gives exception:
Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.
and then I tried:
var results = context.NewsItems
.Where(n => lid.Equals(n.LibraryID));
and got:
Unable to cast the type 'System.Nullable`1' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
and this:
var results = context.NewsItems
.Where(n => object.Equals(lid, n.LibraryID));
gives same exception as the last one.
Now I was desperate, so I tried to complicate stuff (like other forums suggested, for example here):
var results = context.NewsItems
.Where(n => (lid == null ? n.LibraryID == null : n.LibraryID == lid));
but still getting same exception.
So... any SIMPLE workarounds?
How about
var results = context.NewsItems
.Where(n => lid.HasValue ? lid.Value == n.LibraryId.Value : (!n.LibraryId.HasValue) );
Hmm, that first snippet should work. I've used nullables like that many times. First thing I'd do is a sanity check just to make sure LibraryID is really int? and not long? or similar.
Other than that, you can try this:
var results = context.NewsItems
.Where(n => (lid.HasValue ? n.LibraryID == lid.Value : !n.LibraryID.HasValue));
Or to avoid the ?: within the query:
var results = lid.HasValue
? context.NewsItems.Where(n => n.LibraryID == lid.Value)
: context.NewsItems.Where(n => !n.LibraryID.HasValue);
It seems that EF does not find the correct operator overload. Therefore it produces wrong results if you set lid = null.
Use linq to objects by adding AsEnumerable() to your query and everything is fine:
var results = context.NewsItems.AsEnumeryble().Where(n => n.LibraryID == lid);
According to the MSDN docs (which I finally found), .Where() will only filter your collection. If you want to see if there are actually results, resolve by lazily executing the filtered query with .ToList(), GetEnumerator, or enumerating the collection with foreach;
This method is implemented by using deferred execution. The immediate
return value is an object that stores all the information that is
required to perform the action. The query represented by this method
is not executed until the object is enumerated either by calling its
GetEnumerator method directly or by using foreach in Visual C# or For
Each in Visual Basic.
http://msdn.microsoft.com/en-us/library/bb534803.aspx
int? lid = ...
var results = context.NewsItems
.Where(n => n.LibraryID == lid).ToList();
var results = context.NewsItems
.Where(n => n.LibraryID.HasValue && n.LibraryID.Value == lid.Value );
edit:
Previous filter was based on my understanding that you wanted to filter to entires having a particular value. Updated will filter to null or value.
var results = context.NewsItems
.Where(n => !n.LibraryID.HasValue || n.LibraryID.Value == lid.Value );

Categories

Resources