Error iterating the query results - c#

I have a context called companyContext. There are Three tables Reports,Logs and Employees.I am given a case id and I need to get a list of all the employees who belong to a certain case (and a log if there is one, but i dont need to worry about that yet). So I made a query get all the employees where EmployeeID is equal to Employee.ID, where the Reports CaseID is eaual to case.id. However, its not reutning a list, its returning a Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable}]
Am I making the query correctly ? Thanks guys.
var employees = await context.Reports.Include(s => s.Employee)
.ThenInclude(e => e.ID)
.AsNoTracking()
.Where(r => r.CaseID == Case.Id);
Models
public class Log
{
public int ID { get; set; }
public string Input { get; set; }
public string Tag { get; set; }
public DateTime LogDate { get; set; }
public ICollection<Report> Reports { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public ICollection<Report> Reports { get; set; }
}
public class Report
{
public int ID { get; set; }
public string CaseID { get; set; }
public int EmployeeD { get; set; }
public int? LogID { get; set; }
public Employee Employee { get; set; }
public Log Log { get; set; }
}

SingleOrDefaultAsync
throws an exception if there is more than one element in the sequence.
So you should not be using SingleOrDefaultAsync if you expect multiple records.

If I'm interpreting your question properly and you want employees themselves, you could start with them and then narrow via the Employee class's .Reports navigation property.
var employees = await context.Employees.AsNoTracking()
.Where(e => e.Reports.Any(r => r.CaseID == case.Id))
.ToListAsync();

So entity is actually like using a real database. I had to do a where and select clause, but also a toList to convert it into something readable.
var employees = await context.Reports.Where(r => r.CaseID == Case.Id);
.Select(r => r.Employee)
.ToList();

Related

How can I do a self join in ORMLite

I'm trying to get all time entries for a specific foreman based on his supervisor's supervisor. However, I seem to be having trouble writing a self join query in ORMLite. See my data structure and code below.
public class User
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[References(typeof(User))]
public int SupervisorId { get; set; }
}
public class TimeSheet
{
[AutoIncrement]
public int Id { get; set; }
[References(typeof(User))]
public int ForemanId { get; set; }
}
var query = db.From<TimeSheet>()
.Join<User>()
.Join<User, User>(); // not sure how to write this one.
// .Where(super => super.SupervisorId = 2)
I've created a sample gist to try and better show what I've attempted.
var query = db.From<TimeSheet>()
.Join<User>()
.Join<User, User>((p, q) => p.Id == q.SupervisorId, db.JoinAlias("u2"));

Unable to populate an ICollection with related data in Entity Framework

I have tables with created using below models in Entity Framework
public class User
{
public int Id{ get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public ICollection<AssigneeMonth> AssigneeMonths { get; set; }
}
public class AssigneeMonth
{
public int Id { get; set; }
public int AssigneeId { get; set; }
public Month Month { get; set; }
public User Assignee { get; set; }
}
public class ProjectAssignee
{
public int Id { get; set; }
public int ProjectId { get; set; }
public int AssigneeId { get; set; }
public bool IsActive { get; set; }
public AutomationProject Project { get; set; }
[ForeignKey("AssigneeId")]
public User User { get; set; }
}
I am trying to get data into the collection AssigneeMonths from AssigneeMonth using this code:
var assn = dataContext.ProjectAssignees
.Where(r => r.Project.Name == project.Name && r.IsActive)
.Include(u => u.User)
.ToList();
But AssigneeMonths collection in the above assn is always null even if I have data in AssigneeMonth for the user
May I know what's wrong with the above code?
Since you're using eager loading, you're only loading the information for user, not its navigational properties.
You can use the code in this answer, which applied to your case will look like this:
var assn = dataContext.ProjectAssignees
.Where(r => r.Project.Name == project.Name && r.IsActive)
.Include(u => u.User.SelectMany(u => u.AssigneeMonths))
.ToList();
Since AssigneeMonths is a collection, you need to use SelectMany instead of Select.
Other option would be to do it like this (as posted in other answers in that link):
var assn = dataContext.ProjectAssignees
.Where(r => r.Project.Name == project.Name && r.IsActive)
.Include(u => u.User)
.Include("User.AssigneeMonths")
.ToList();
But I personally don't like the second method because its easy to make mistakes with the string and it will hide the errors until runtime.

Entity Framework materializes collections inside IQueryable methods before their execution

On my previous work EF keeps query as IQueryable until I used some materialized methods, for example, ToList() or FirstOrDefault(), and I could create big, flexible and fast queries. But on my new work I noted, that sequences inside IQueryable methods have ICollection type and, of course, they have IEnumerable methods (not IQueriable). I can't understand what wrong and how I can change it. I haven't find solution in google. The version of EF is the same as on my previous work (6.1.3).
For Example, I have 2 entity classes and my own class:
public class Client // Entity
{
public int ID { get; set; }
public string FullName { get; set; }
public string Address { get; set; }
...
public virtual ICollection<Parcel> ParcelsSender { get; set; }
public virtual ICollection<Parcel> ParcelsReceiver { get; set; }
}
public class Parcel // Entity
{
public int ID { get; set; }
public string ParcelNumber { get; set; }
...
public int ClientSenderID { get; set; }
public int ClientReceiverID { get; set; }
public virtual Client ClientSender { get; set; }
public virtual Client ClientReceiver { get; set; }
}
public class ClientCustom // My class
{
public int ID { get; set; }
public string FullName { get; set; }
public bool IsInexperienced { get; set; }
}
and I created this EF query:
var clients = context.Clients
.OrderBy(x => x.FullName)
.Select(x => new ClientCustom()
{
ID = x.ID,
FullName = x.FullName,
IsInexperienced = x.ParcelsSender.Select(y => y.ID).FirstOrDefault() == 0
&& x.ParcelsReceiver.Select(y => y.ID).FirstOrDefault() == 0
})
.ToList();
In this case, the problem is that x.ParcelsSender and x.ParcelsReceiver in query are ICollection<Parcel> type; In turn, x.ParcelsSender.Select() and x.ParcelsReceiver.Select() methods returns IEnumerable<Parcel> instead IQueriable<Parcel>. As I know, that means, queries become very slowly with big amount of data.

Get data from nested table using entity framework

First of all this is my first question in the forum so please excuse me for any writing mistake.
I have 4 tables
attaching the table diagram
What I want is to get list of attraction name joining 'tblattraction' with 'tblattractionmaster' and count of the exact attraction for each place from 'tblattractions' using 'locationid' , I am using entity framework but don't know how to do that,
Disclaimer:
Each location can consist Multiple Places
Each Place can consist Multiple Attractions
What I have tried
return context.tblLocationMasters.Select(t => new details()
{
locationid = t.LocationId,
locationname = t.LocationName,
attractions =t.tblPlaces.SelectMany(a => a.tblAttractions).Select(b => new attractions(){
AttractionName=b.tblAttractionMaster.attractionname//(Not working),
TotalAttractions=0//???
}).ToList()
}).ToList();
I recreated your model (slightly different) using Code First. I came up with the following structure:
public class Location
{
public int LocationId { get; set; }
public string LocationName { get; set; }
public ICollection<Place> Places { get; set; }
}
public class Place
{
public int PlaceId { get; set; }
public string PlaceName { get; set; }
public int LocationId { get; set; }
public Location Location { get; set; }
public ICollection<AttractionPlace> Attractions { get; set; }
}
public class Attraction
{
public int AttractionId { get; set; }
public string AttractionName { get; set; }
}
public class AttractionPlace
{
public int AttractionPlaceId { get; set; }
public int PlaceId { get; set; }
public Place Place { get; set; }
public int AttractionId { get; set; }
public Attraction Attraction { get; set; }
}
Then, I could get the results in the way you needed with the following query:
var query = (from loc in db.Locations
join pla in db.Places.Include(x => x.Attractions) on loc.LocationId equals pla.LocationId
let count = pla.Attractions.Count()
select new
{
loc.LocationId,
loc.LocationName,
Attractions = pla.Attractions.Select(z => new
{
pla.PlaceName,
z.AttractionId,
z.Attraction.AttractionName
}),
AttractionsByPlaceCount = count
});
The query above returns data in this format
Just a side note though: I didn't went further to see the performance of this query. The SQL generated by Linq wasn't that bad, but you should consider analyzing it before actually using it in production.

How can I user LINQ to get a IList<string> from a multi-table join?

I have a SQL Server 2012 database and I am using Entity Framework 6.1 to access data.
I have the following entities that are joined to each other with Primary Key and Foreign Key relationships. What I would like to do is to get a simple collection of values of the Quid parameter in the Questions table where the taskId matches a value that is chosen outside of this code.
Task contains Objective contains ObjectiveDetail contains SubTopic contains Problems contations
Questions
I created the following LINQ statement. It seems not to work and I need some advice on what I am doing wrong and how the statement could be made to get what I need. In particular I am not sure about the way I do the join with all the many .Selects and also the select to give me the output.
var quids = db.Tasks
.Include(e => e.Objectives
.Select(o => o.ObjectiveDetails
.Select(od => od.SubTopics
.Select(s => s.Problems
.Select(p => p.Questions)))))
.Where(t => t.TaskId == taskId)
.Select(e => e.Objectives
.Select(o => o.ObjectiveDetails
.Select(od => od.SubTopics
.Select(s => s.Problems
.Select(p => p.Questions
.Select(q => q.QuestionUId))))))
.ToList();
However this gives me a very confusing output and certainly not the simple IList of Quids
that I would like. Here are my classes for reference. I removed additional fields and hopefully just left the important ones.
public class Task
{
public Task()
{
this.Objectives = new HashSet<Objective>();
}
public int TaskId { get; set; }
public virtual ICollection<Objective> Objectives { get; set; }
}
public class Objective
{
public Objective()
{
this.ObjectiveDetails = new HashSet<ObjectiveDetail>();
}
public int ObjectiveId { get; set; }
public int TaskId { get; set; }
public virtual Task Task { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
public partial class ObjectiveDetail
{
public ObjectiveDetail()
{
this.SubTopics = new HashSet<SubTopic>();
}
public int ObjectiveDetailId { get; set; }
public int ObjectiveId { get; set; }
public virtual Objective Objective { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public class SubTopic
{
public SubTopic()
{
this.Problems = new HashSet<Problem>();
}
public int SubTopicId { get; set; }
public int Number { get; set; }
public int TopicId { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
public virtual ICollection<Problem> Problems { get; set; }
}
public class Problem
{
public Problem()
{
this.Questions = new HashSet<Question>();
}
public int ProblemId { get; set; }
public int SubTopicId { get; set; }
public virtual SubTopic SubTopic { get; set; }
public virtual ICollection<Question> Questions { get; set; }
}
public class Question
{
public int QuestionId { get; set; }
public string QuestionUId { get; set; }
public virtual Problem Problem { get; set; }
}
Please note the many-many relationship between SubTopic and ObjectiveDetail. This I think makes it more difficult.
From what I understand of your need, you can simply start from the Question DbSet instead of the task:
var uids = db.Questions.Where(q => q.Problem.SubTopic
.ObjectiveDetails
.Any(od => od.Objective.TaskId == taskId))
.Select(q => q.QuestionUId)
.ToList();
This will take all Question which has at least one objective with the TaskId equal to taskId then project the QuestionUid and put it in a list.
Each of your outer Select calls produces a sequence for each item. This gives you nested sequences and probably a confusing ToString debug output.
What you probably meant was to use SelectMany to flatten the nested sequences.
The Include in your query cannot possibly help because you are just returning a list of strings. There is nothing to include in a string. Better remove it. Who knows what code EF generates from it.

Categories