Returning DbQuery to view that requires IEnumerable - asp.net-mvc

The problem:
Exception Details: System.InvalidOperationException: The model item
passed into the dictionary is of type
'System.Data.Entity.Infrastructure.DbQuery`1[System.Int32]', but this
dictionary requires a model item of type 'migros.Models.State'.
What I'm trying to do
I need to pass the result of the following linq query to a View.
using (var db = new migros_mockEntities1())
{
var listOfIdeas = (from x in db.States select x.ID);
return View(listOfIdeas);
}
The View requires IEnumerable, but it seems I can't cast the result of the linq query to IEnumerable.
I'm using entity framework database first approach.

The problem is that you trying to return ObjectQuery from within the using block.
Try to materialize your object-set
var listOfIdeas = (from x in db.States select x.ID).ToList();
Also, dont forget, that dealing with context can be tricky.
In your case var listOfIdeas = (from x in db.States select x.ID) is just a query, that will run only when you'd begin to iterate over it. So, if context gets already disposed you'd get an exception, trying to use listOfIdeas.

Related

EF Core 2.0 - filtering IQueryable navigation property collection empty

Tech:
EF Core 2.0.0
Asp.Net Core Mvc 2.0.0
When i execute this method it throws "InvalidOperationException: Sequence contains no matching element" on evaluating CurrentGrade. Why does it throw and how can I fix it?
I have a search method filtering on quite a lot of properties on a large dataset (10 000 users with thousands of related entities). I'm trying to optimize the query and i don't want to execute the query until all filtering has been made. While using ToList() makes the method work i would rather work against an IQueryable and execute the query when done filtering.
I'm pretty sure this worked before updating EF Core from 1.x to 2.0.
public MemberQueryResult Search(MemberQuery filter)
{
var query = Context.Users
.Include(x => x.Honours)
.Include(x => x.Grades)
.Include(x => x.Strokes)
.Include(x => x.Posts)
.Include(x => x.Loge)
.AsNoTracking();
query = query.ApplyFiltering(filter);
return result;
}
ApplyFiltering() works well for filtering on foreign keys but when filtering on a navigation property collection using .Where() it throws on ICollection Grades on Member when just before the filtering it was included.
This is the method inside ApplyFiltering() that throws:
private static IQueryable<Member> SearchByCurrentGradeRange(MemberQuery filter, IQueryable<Member> result)
{
if (filter.GradeRange == null) return result;
var gradeRange = filter.GradeRange.Split(',');
var gradeFrom = (Grade)int.Parse(gradeRange[0]);
var gradeTo = (Grade)int.Parse(gradeRange[1]);
result = result.Where(x => x.CurrentGrade >= gradeFrom && x.CurrentGrade <= gradeTo);
return result;
}
CurrentGrade is a calculated property on a member, Grade is just an enum.:
public sealed class Member : IdentityUser
{
public Grade CurrentGrade => Grades.OrderBy(x => x.Grade).Last(x => x.ReceivedDate != null).Grade;
public ICollection<MemberGrade> Grades { get; set; } = new Collection<MemberGrade>();
}
The problem is that the unmapped ("calculated") property is causing client evaluation, but at the time EF evaluates the client part of the Where clause, the navigation properties are not loaded yet, hence your Grades collection is empty (as it has been iinitialized with new Collection<MemberGrade> - if you remove the initializer, then you'll get NullReferenceException).
Now, probably it could be treated as EF Core bug. But I strongly recommend not using unmapped properties in LINQ queries in general, and especially in query filter conditions. Even if they work, the client evaluation will cause loading a lot of data in memory just to apply the filter there, rather than at the database (SQL) side.
Also make sure to use SQL translatable constructs. For instance, Last / LastOrDefault have no natural SQL translation, while FirstOrDefault does, so the usual pattern is OrderByDescending().FirstOrDefault() rather than OrderBy().LastOrDefault().
With that being said, the working server side evaluating solution in your case would be like this:
result = result.Where(m => m.Grades
.Where(x => x.ReceivedDate != null).OrderByDescending(x => x.Grade).Take(1)
.Any(x => x.Grade >= gradeFrom && x.Grade <= gradeTo));

EntityFrameworkCore FromSql method call throws System.NotSupportedException

So am using AspNetCore 1.0 with EFCore 1.0, both latest releases as far as I am aware.
Executing a query to delete an object using the FromSql method on a DbSet throws an exception. Both the code and exception are below.
public void DeleteColumn(int p_ColumnID)
{
int temp = p_ColumnID;
string query = "DELETE FROM Columns WHERE ID = {0}";
var columnsList = m_context.Columns.FromSql(query, p_ColumnID).ToList();
foreach (Columns c in columnsList)
{
m_context.Columns.Remove(c);
}
m_context.SaveChanges();
}
After executing the FromSql call, I get the following exception
An exception of type 'System.NotSupportedException' occurred in Remotion.Linq.dll but was not handled in user code
Additional information: Could not parse expression 'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[ASPNET5_Scrum_Tool.Models.Columns]).FromSql("DELETE FROM Columns WHERE ID = {0}", __p_0)': This overload of the method 'Microsoft.EntityFrameworkCore.RelationalQueryableExtensions.FromSql' is currently not supported.
I have no clue how to fix this error and from Googling I have come across no similar problems.
I am also wondering, if the query/code was successful it would return an 'IQueryable object. Would that solely contain the results of the query, in this case the specific Column object to delete?
FromSql is intended to allow you to compose a custom SQL SELECT statement that will return entities. Using it with a DELETE statement is not appropriate here, since your goal is to load the records you want to delete and then delete them using the default Entity Framework mechanism. A Delete statement generally does not return the records deleted (though there are ways to accomplish that). Even if they did, the records will already be deleted and so you won't want to iterate over them and do a Remove on them.
The most straightforward way to do what you want might be to use the RemoveRange method in combination with a Where query.
public void DeleteColumn(int p_ColumnID)
{
m_context.Columns.RemoveRange(m_context.Columns.Where(x => x.ID == p_ColumnID))
m_context.SaveChanges();
}
Alternately, if you want to load your entities and iterate manually through them to
public void DeleteColumn(int p_ColumnID)
{
columnList = m_context.Columns.Where(x => x.ID == p_ColumnID);
foreach (Columns c in columnsList)
{
m_context.Columns.Remove(c);
}
m_context.SaveChanges();
}
If you really want to issue the Delete statement manually, as suggested by Mike Brind, use an ExecuteSqlCommand method similar to:
public void DeleteColumn(int p_ColumnID)
{
string sqlStatement = "DELETE FROM Columns WHERE ID = {0}";
m_context.Database.ExecuteSqlCommand(sqlStatement, p_ColumnID);
}
I had the same exception in a case where I did not use delete statement. Turns out I was using the In-Memory Database. Since it is not a real database you can't use FromSQL.

How to reference enum type in entity sql

I have the following (simplified) Entity SQL query:
SELECT VALUE a
FROM Customers AS a
WHERE a.Status NOT IN { 2, 3 }
The Status property is an enumeration type, call it CustomerStatus. The enumeration is defined in the EDMX file.
As it is, this query doesn't work, throwing an exception to the effect that CustomerStatus is incompatible with Int32 (its underlying type is int). However, I couldn't find a way to define a list of CustomerStatus values for the IN {} clause, no matter what namespace I prefixed to the enumeration name. For example,
SELECT VALUE a
FROM Customers AS a
WHERE a.Status NOT IN { MyModelEntities.CustomerStatus.Reject, MyModelEntities.CustomerStatus.Accept }
did not work, throwing an exception saying it could not find MyModelEntities.CustomerStatus in the container, or some such.
Eventually I resorted to casting the Status to int, such as
SELECT VALUE a
FROM Customers AS a
WHERE CAST(a.Status AS System.Int32) NOT IN { 2, 3 }
but I was hoping for a more elegant solution.
Oooh, you are writing Entity SQL directly. I see... Any reason you aren't using DbSet instead of writing Entity SQL by hand? Could always do
var statuses = new [] { Status.A, Status.B };
var query = context.SomeTable.Where(a => !statuses.Contains(a.Status)).ToList();

LLBLGenProQuery Vs IQueryable

I am using LLBLGen Pro, MVC 3 and VB.NET. I am trying to return an object of Iqueryable but i am getting following exception
Unable to cast object of type 'SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1[Mail.DAL.EntityClasses.TblCostCentreEntity]' to type 'System.Linq.IQueryable1[Mail.Model.CostCentre]'.
I am not sure whats the difference betweeen LLBLGenProQuery and IQueryable? How do i return an IQueryable object using LLBLGenPro? My code is as following:
Public Function GetAllCostCentres() As IQueryable(Of Model.CostCentre) Implements ICostCentreRepository.GetAllCostCentres
Mapper.CreateMap(Of TblCostCentreEntity, CostCentre)()
Dim metaData As New LinqMetaData
Dim q = From p In metaData.TblCostCentre _
Select Mapper.Map(Of IQueryable(Of CostCentre), TblCostCentreEntity)(p)
'Dim t As IQueryable(Of CostCentre) = Mapper.Map(Of CostCentre)(q)
'Select New CostCentre With {.Active = p.Active, .CostCentre = p.CostCentre, .CreatedBy = p.CreatedBy, .DateCreated = p.DateCreated, .DateLastModified = p.DateLastModified, .ModifiedBy = p.ModifiedBy, .CostCentreID = p.CostCentreId}
Return q
End Function
IQueryable
(http://llblgen.com/documentation/3.5/LLBLGen%20Pro%20RTF/hh_goto.htm#Using%20the%20generated%20code/Linq/gencode_linq_gettingstarted.htm#LinqMetaData)
It's a query that will make the compiler produce code which creates at runtime a tree of Expression instances, representing the entire query, in short an Expression tree. An Expression tree is not executable directly, it has to be interpreted to execute what is specified inside the Expression tree. This is what a Linq provider, like Linq to LLBLGen Pro, does: it accepts an Expression tree, translates it into elements it can understand, interprets these elements and produces an executable form of this query.
ILLBLGenProQuery
(http://llblgen.com/documentation/3.5/LLBLGen%20Pro%20RTF/hh_goto.htm#Using%20the%20generated%20code/Linq/gencode_linq_gettingstarted.htm#ILLBLGenProQuery)
An interface defined on the IQueryable elements produced by LinqMetaData. This interface allows you to execute the query by calling the Execute method. The advantage of this is that you can get the query result in its native container, e.g. an entity collection. Another advantage is that to obtain a list of the results, the provider doesn't have to traverse the results in full, and copy over the results in a List: the returned results are already in the container they're initially stored in.

Getting only what is needed with Entity Framework

With a plain connection to SQL Server, you can specify what columns to return in a simple SELECT statement.
With EF:
Dim who = context.Doctors.Find(3) ' Primary key is an integer
The above returns all data that entity has... BUT... I would only like to do what you can with SQL and get only what I need.
Doing this:
Dim who= (From d In contect.Doctors
Where d.Regeneration = 3
Select New Doctor With {.Actor = d.Actor}).Single
Gives me this error:
The entity or complex type XXXXX cannot be constructed in a LINQ to Entities query.
So... How do I return only selected data from only one entity?
Basically, I'm not sure why, but Linq can't create the complex type. It would work if you were creating a anonymous type like (sorry c# code)
var who = (from x in contect.Doctors
where x.Regeneration == 3
select new { Actor = x.Actor }).Single();
you can then go
var doctor = new Doctor() {
Actor = who.Actor
};
but it can't build it as a strongly typed or complex type like you're trying to do with
var who = (from x in contect.Doctors
where x.Regeneration == 3
select new Doctor { Actor = x.Actor }).Single();
also you may want to be careful with the use of single, if there is no doctor with the regeneration number or there are more than one it will throw a exception, singleordefault is safer but it will throw a exception if there is more than one match. First or Firstordefault are much better options First will throw a exception only if none exist and Firstordefault can handle pretty much anything
The best way to do this is by setting the wanted properties in ViewModel "or DTO if you're dealing with upper levels"
Then as your example the ViewModel will be:
public class DoctorViewModel{
public string Actor {get;set;}
// You can add as many properties as you want
}
then the query will be:
var who = (From d In contect.Doctors
Where d.Regeneration = 3
Select New DoctorViewModel {Actor = d.Actor}).Single();
Sorry i wrote the code with C# but i think the idea is clear :)
You can just simply do this:
Dim who= (From d In contect.Doctors
Where d.Regeneration = 3
Select d.Actor).Single
Try this
Dim who = contect.Doctors.SingleOrDefault(Function(d) d.Regeneration = 3).Actor

Resources