NotSupportedException when converting an IQueryable<object> - c#

When I do this :
var db = new NotentoolEntities();
IQueryable<GroupOfBranches> queryGOB = db.tabGroupOfBranches.Cast<GroupOfBranches>().Where(x => x.intGroupID.Equals(ID));
List<GroupOfBranches> GOB = new List<GroupOfBranches>(queryGOB); //here is the error
I've got the following error :
A first chance exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll

I suppose that the underlying LINQ provider cannot convert call to Equals to anything it knows about.
Use == operator instead. That will end up in a different expression tree, which can be translated.
var db = new NotentoolEntities();
IQueryable<GroupOfBranches> queryGOB =
db.tabGroupOfBranches.Cast<GroupOfBranches>().Where(x => x.intGroupID == ID));
List<GroupOfBranches> GOB = new List<GroupOfBranches>(queryGOB);
If GroupOfBranches is derived class from whatever the type is returned by tabGroupOfBranches, then you should probably use OfType, rather than Cast.
Otherwise, Cast might go after Where, and be replaced with a Select which creates instances of GroupOfBranches class explicitly.

Might consider to change your code to
using (var db = new NotentoolEntities())
{
var GOB = db.tabGroupOfBranches.Where(x => x.intGroupID == ID).Select(y => new GroupOfBranches
{
/// here specify your fields
}).AsNoTracking().ToList();
}

Related

How to concat multiple selects with LINQ?

I have main select which looks like this:
protected IQueryable<Answers> GetActualAnswers<TAns>(DateTime? start, DateTime? end, long? statusId) where TAns: AnswersBase
{
_contex.Set<TAns>.Where(x => x.Type == VoteType.Good)
.Select(vv => new Answers
{
CreatedAt = vv.CreatedAt,
StatusId = vv.StatusId,
Type = vv.Type ,
AnswerInGuideStatusId = vv.AnswerInGuideStatusId
}
}
I'm using this method in two simple queries:
var result1 = GetActualAnswers<JournalAnswers>(start, end, statusId)
.Select(j => new UnitedAnswers
{
Question = j.Question,
}
var result2 = GetActualAnswers<BoAnswers>(start, end, statusId)
.Select(b => new UnitedAnswers
{
Prospects = b.Prospects ,
}
var mainResult = result1.Concat(result2);
I get errors:
Sql = Sql = '((System.Data.Entity.Infrastructure.DbQuery<UnitedAnswers>)result1).Sql' threw an exception of type 'System.NotSupportedException'
Sql = Sql = '((System.Data.Entity.Infrastructure.DbQuery<UnitedAnswers>)result2).Sql' threw an exception of type 'System.NotSupportedException'
Sql = Sql = '((System.Data.Entity.Infrastructure.DbQuery<UnitedAnswers>)mainResult).Sql' threw an exception of type 'System.NotSupportedException'
Is it possible to use several Selects? May be someone can give advice with this query?
Firstly, I was wondering where the property j.Question and b.Prospects you get, While in the method GetActualAnswers you did not get the value of 2 properties above.
Secondly, At the method GetActualAnswers you were returning IQueryable, so you should check empty instead of null value
Then your case might look like this
var mainResult = Enumerable.Concat(
resut1 ?? Enumerable.Empty<UnitedAnswers>(),
resut2 ?? Enumerable.Empty<UnitedAnswers>()
or
var mainResult = Enumerable.Concat(
result1.AsEnumerable(),
result2.AsEnumerable());
The following links are useful for you.
How to merge two IQueryable lists
Enumerable.AsEnumerable

Linq - EntityFramework NotSupportedException

I have a query that looks like this:
var caseList = (from x in context.Cases
where allowedCaseIds.Contains(x => x.CaseId)
select new Case {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
Notifier = x.NotifierId.HasValue ? new Notifier { Name = x.Notifier.Name } : null // This line throws exception
}).ToList();
A Case class can have 0..1 Notifier
The query above will result in the following System.NotSupportedException:
Unable to create a null constant value of type 'Models.Notifier'. Only entity types, enumeration types or primitive types are supported
in this context.
At the moment the only workaround I found is to loop the query result afterwards and manually populate Notifierlike this:
foreach (var c in caseList.Where(x => x.NotifierId.HasValue)
{
c.Notifier = (from x in context.Notifiers
where x.CaseId == c.CaseId
select new Notifier {
Name = x.Name
}).FirstOrDefault();
}
But I really don't want to do this because in my actual scenario it would generate hundreds of additional queries.
Is there any possible solution for a situation like this?.
I think you need to do that in two steps. First you can fetch only the data what you need with an anonymous type in a single query:
var caseList = (from x in context.Cases
where allowedCaseIds.Contains(x => x.CaseId)
select new {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
NotifierName = x.Notifier.Name
}).ToList();
After that, you can work in memory:
List<Case> cases = new List<Case>();
foreach (var c in caseList)
{
var case = new Case();
case.CaseId = c.CaseId;
case.NotifierId = c.NotifierId;
case.NotifierName = c.NotifierId.HasValue ? c.NotifierName : null;
cases.Add(case);
}
You could try writing your query as a chain of function calls rather than a query expression, then put an .AsEnumerable() in between:
var caseList = context.Clases
.Where(x => allowedCaseIds.Contains(x.CaseId))
.AsEnumerable() // Switch context
.Select(x => new Case() {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
Notifier = x.NotifierId.HasValue
? new Notifier() { Name = x.Notifier.Name }
: null
})
.ToList();
This will cause EF to generate an SQL query only up to the point where you put the .AsEnumerable(), further down the road, LINQ to Objects will do all the work. This has the advantage that you can use code that cannot be translated to SQL and should not require a lot of changes to your existing code base (unless you're using a lot of let expressions...)

Join on Identity and Another Table

I am trying to figure out how do I create a join statement on Identity and another table that I have, in this case TeamMember.
Here is what I have tried:
var j =
Db.TeamMembers.Join(MemberManager.Users,
c => c.MemberId,
cm => cm.Id,
(c, cm) => new {TeamMember = c, Member = cm})
.Where(m => m.TeamMember.TeamId == team.Id && !m.TeamMember.MemberId.Equals(team.CaptainId));
List<TeamMember> teamMembers = new List<TeamMember>();
foreach (var result in j)
{
var teamMember = new TeamMember
{
GameDisplayName = result.Member.Alias
};
teamMembers.Add(teamMember);
}
And the error that I have is
The specified LINQ expression contains references to queries that are associated with different contexts.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NotSupportedException: The specified LINQ expression contains references to queries that are associated with different contexts.
It's saying that MemberManager and Db are different objects. It would have to query one and pass the results to the other in order to make this function (imagine if they pointed to different databases), and it's not willing to do that by default, because it'd be a bad idea more often than not.
The simple option is just to change MemberManager.Users to Db.Users. If I understand your setup correctly, that should fix it.
That said, I don't understand why MemberManager exists at all, so maybe I don't understand it. In that case, you'd have to do something like this:
var team = /*...*/;
var teamMembers = Db.TeamMembers
.Where(c => c.TeamId == team.Id && c.MemberId != team.CaptainId)
.ToList();
var teamMemberIds = teamMembers.Select(c => c.MemberId);
var members = MemberManager.Users
.Where(c => teamMemberIds.Any(x => c.Id == x))
.ToList();
var j = teamMembers
.Join(members, c => c.MemberId, cm => cm.Id, (c, cm) => new
{
TeamMember = c,
Member = cm
});
It'd be best to avoid this if you can, since it is two separate server calls. But it does let you use multiple contexts.

Error: System.Collections.Generic.List<AnonymousType#1>

I'm getting the error. Here's my function:
public List<PlainBrgMetric> GetPlainBrgMetricProgram(long programLOBID)
{
var query = _context.metrics.Join(_context.universals,
m => m.metricID,
u => u.orderByAsc,
(metric, universal) => new
{
metric.metricID,
metric.programLOBID,
metric.label,
universal.groupValue1
}).ToList();
return query;
}
To fix it you must return a list of PlainBrgMetric, what you are returning is a list of anonymous objects.
You should edit your code as follows:
public List<PlainBrgMetric> GetPlainBrgMetricProgram(long programLOBID)
{
var query = _context.metrics.Join(_context.universals,
m => m.metricID,
u => u.orderByAsc,
(metric, universal) => new PlainBrgMetric
{
//Populate the object properties
...
}).ToList();
return query;
}
This is the expected behavior, because here:
(metric, universal) => new
{
metric.metricID,
metric.programLOBID,
metric.label,
universal.groupValue1
}
you create an anonymous type and not a PlainBrgMetric object.
Provided that PlainBrgMetric has at least the same four properties as the anonymou's type properties, you create, you could make a quick fix:
(metric, universal) => new PlainBrgMetric
{
MetricID = metric.metricID,
ProgramLOBID = metric.programLOBID,
Label = metric.label,
GroupValue1 = universal.groupValue1
}
Otherwise you have to declare another type with these four properties and change both the signature of your method and the type you create above for each result of the join.
I didn't mention the alternative of the dynamic object, since I assumed from your code that you want to return a collection of strongly typed objects.

Problem with LINQ to Entities query using Sum on child object property

Given this query:
from s in services
select new
{
s.Id,
s.DateTime,
Class = s.Class.Name,
s.Location,
s.Price,
HeadCount = s.Reservations.Sum(r => r.PartySize), // problem here. r.PartySize is int
s.MaxSeats
}
If the service doesn't have any reservations, this exception is thrown:
System.InvalidOperationException: 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.
I get it, but how should I deal with it? My intention is if there are no reservations, then HeadCount be assigned 0.
There's an even simpler solution:
from s in services
select new
{
s.Id,
s.DateTime,
Class = s.Class.Name,
s.Location,
s.Price,
HeadCount = (int?)s.Reservations.Sum(r => r.PartySize),
s.MaxSeats
}
Note the cast. This may also produce simpler SQL than #Ahmad's suggestion.
Essentially, you're just helping out type inference.
You should check for it:
HeadCount = s.Reservations != null ? s.Reservations.Sum(r => r.PartySize) : 0,
This should resolve your problem:
Try to cost the int to int?
from s in services
select new
{
s.Id,
s.DateTime,
Class = s.Class.Name,
s.Location,
s.Price,
HeadCount = s.Reservations.Sum(r => (int?) r.PartySize),
s.MaxSeats
};
HeadCount = HeadCount ?? 0;
A simple ternary operator should fix the problem nicely...
something like this:
HeadCount = (s.Reservations != null && s.Reservations.Any()) ? s.Reservations.Sum(r => r.PartySize) : 0;
This will handle for both null and empty situations

Categories