How should I order nested entities when I load parent entity? - c#

I have entities similar to this:
public class Search
{
public int ID { get; set; }
public bool Complete { get; set; }
public int Processed { get; set; }
public virtual ICollection<PostCode> PostCodes { get; set; }
}
public class PostCode
{
public int ID { get; set; }
public string Value { get; set; }
}
I want to make sure that the PostCode collection is always ordered by its ID field when I load a Search entity.
Is there a way to set an order for nested collection when I load it?
There is many thousand postcodes so it is expensive process to iterate over whole list. In the below code am I iterating over every postcode twice or is this the right way to do it?
Search s = db.Searches.Include("PostCodes").FirstOrDefault(x => x.Complete == false);
s.PostCodes = s.PostCodes.OrderBy(x => x.ID).ToList();
I haven't profiled it but it feels like it's taking longer now.

Related

Linq join with Many to Many

I'm using MVC, C# and EntityFramework.
I've seen different solutions on Many to Many joins and after a lot of tinkering I got it to work in Linqpad. But when I try it in my solution I get an error because one of the tables isn't in my DBContext.
I have two visible tables and one hidden. Items, Recipes & RecipeItems.
All recipes are based on one item and use two or more items to be made.
So I want a list, IEnumerable or similar with the data from both Items and Recipes that specifies this recipe and then I want all the items needed to make the recipe.
The following query works in LinqPad
var t = from r in Recipes
join i in Items on r.ItemId equals i.Id
select new {FinalProduct = r.FinalProduct, Effect= i.Effect,
Description = r.Description, Ingredients = r.RecipeItems.Select(g => g.Item)};
When I do this in my solution I get the error since my DBContext only contains Recipe and Items but no RecipeItems. Entityframework handles this without me I guess.
I tried to make a DbSet<RecipeItems> without any luck. Any of you who have a suggestion of how I can move forward.
Item Class
public class Item
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Effect { get; set; }
public bool Published { get; set; }
public virtual ICollection<Recipe> Recipe { get; set; }
}
Recipe Class
public class Recipe
{
[Key]
public int Id { get; set; }
public int ItemId { get; set; }
[Display(Name = "Final Product")]
public string FinalProduct { get; set; }
public string Description { get; set; }
public RecipeGroup RecipeGroup { get; set; }
public bool Published { get; set; }
public virtual ICollection<Item> Ingredients { get; set; }
}
The ItemId in Recipe is to set the actual Item the Recipe will make.
Try adding this to your Recipe object:
public Recipe()
{
this.Ingredients = new HashSet<Item>();
}
This overrides the default constructor for the class and kind of gives EF a place that initializes the related objects.

Searching through a string or xml type in entity framework, what is the fastest?

I'm using entity framework and I have a string field I'm using to hold ids separated by commas. There will never be more than ~30 ids in any string, but I might have millions of rows to search.
ex. "435,6789,1231,232"
I know I can search through the string for specific ids like this below with .Contains()
var events = dbContext.YogaSpaceEvents.Where(i => i.RegisteredStudentIds.Contains("12345") && i.EventDateTime >= DateTime.Now);
But will this run really slowly with millions of records?
If so, is there a better approach or column type I can use, like xml that will run much faster when I have millions of rows?
It's hard for me to test because I would need to populate millions of rows!
It will probably run slower than needed. Just create a table named Registrations with EventId and StudentId as the primary key and you'll be rocket fast :)
On the code side, it will looks something like:
var student = dbContext.Students.FindById(12345);
var events = student.Events.Where(i.EventDateTime >= DateTime.Now);
here is what I have so far, I have an event and I want to keeps records of the students registered so I can say "what events is student '12345' registered for?" and then return those events
public class YogaSpaceEvent
{
public YogaSpaceEvent() {}
[Key]
public int YogaSpaceEventId { get; set; }
[Index]
public int YogaSpaceRefId { get; set; }
[ForeignKey("YogaSpaceRefId")]
public virtual YogaSpace YogaSpace { get; set; }
[Required]
[Index]
public DateTime EventDateTime { get; set; }
[Required]
public YogaTime Time { get; set; }
[Required]
public YogaSpaceDuration Duration { get; set; }
public virtual ICollection<RegisteredStudents> RegisteredStudents { get; set; }
}
Here is RegisteredStudents
public class RegisteredStudents
{
public RegisteredStudents () {}
[Key]
public int RegisteredStudentsId { get; set; }
[Index]
public int YogaSpaceEventRefId { get; set; }
[ForeignKey("YogaSpaceEventRefId")]
public virtual YogaSpaceEvent YogaSpaceEvent { get; set; }
public int StudentId { get; set; }
}
and now I can say
var events = dbContext.RegisteredStudents.Where(i => i.StudentId == 12345).Select(j => j.YogaSpaceEvent);

Grouping hierarchical data with LINQ

I have a search view that needs to return a grouping of all employees based on their level in the organization hierarchy. The user needs to have the ability to group by any level in this hierarchy. So for example, I have drop down check lists that allow 1 or more options to be selected for Division, Department, Section, Group.
The way that I have the organization structure represented in my data model is like this:
public class OrganizationEntity : IEntity
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int OrganizationEntityTypeId { get; set; }
[DataMember]
[ForeignKey("OrganizationEntityTypeId")]
public virtual OrganizationEntityType OrganizationEntityType { get; set; }
[DataMember]
public virtual OrganizationEntity Parent { get; set; }
public virtual ICollection<OrganizationEntity> Children { get; set; }
}
The OrganizationEntityType tells me where in the organization hierarchy I am.
Employees are only linked to one organization entity so employee model looks like:
public class Employee : IEntity
{
[Key]
[DataMember]
public int Id { get; set; }
[Required]
[DataMember]
public string Name { get; set; }
[DataMember]
public int CityId { get; set; }
[DataMember]
[ForeignKey("CityId")]
public virtual City City { get; set; }
[DataMember]
public int OrganizationEntityId { get; set; }
[DataMember]
[ForeignKey("OrganizationEntityId")]
public virtual OrganizationEntity OrganizationEntity { get; set; }
}
Now I'm trying to figure out the best way to group by a level in the org hierarchy using LINQ. So say the user selects Division 1, Division 2, All Sections, Group 1 and has their grouping set to Divisions I would need to see the data looks something like this:
Divisions Employee Count
division1 25
division2 3
And if the same parameters where the same except the grouping was set to Section the data would look like:
Sections Employee Count
Section1 15
Section2 3
Section3 4
And so on for the other levels.
Here is a sample dataset:
Any advice would be very much appreciated.
Update
Added a filter to the employees query to only include those in the Org level being group by and all of those below it and now works great! Code looks like this:
var employees = context.Employees.Where(o => orgEntityIds.Contains(o.OrganizationEntityId));
LINQ can do this with the GroupBy method, but you'll have to be a bit creative with the way you generate the grouping key. Here's a rough idea:
Func<Employee, string> selector = (n => FindOrganizationName(n.OrganizationEntity, orgType));
Dictionary<string, int> results = employees.GroupBy(selector)
.ToDictionary(n => n.Key, n => n.Count());
where FindOrganizationName is a recursive function that crawls up the organization hierarchy until it finds the requested organization type, like this:
string FindOrganizationName(OrganizationEntity entity, int entityType)
{
if (entity == null)
{
return string.Empty;
}
else if (entity.OrganizationEntityTypeId == entityType)
{
return entity.Name;
}
else
{
return FindOrganizationName(entity.Parent, entityType);
}
}

Entity Framework returns null for Include properties

I got 3 entities(tables) that have many to many connections:
public class AccUserRole
{
public long Id { get; set; }
public string RoleName { get; set; }
public List<AccAdGroup> Groups { get; set; }
public List<AccScreen> Screens { get; set; }
}
public class AccAdGroup
{
public long Id { get; set; }
public string AdIdent { get; set; }
public List<AccUserRole> Roles { get; set; }
}
public class AccScreen
{
public long Id { get; set; }
public string ScreenIdent { get; set; }
public List<AccUserRole> Roles { get; set; }
}
I wanted to get all Roles(including their screens and groups) that has at least one of specified list of groups(the groups of the current user). So I used this query:
List<AccUserRole> userRoles = (from ur in db.AccUserRoles.Include("Groups").Include("Screens")
from g in ur.Groups
where user.Groups.Contains(g.AdIdent)
select ur).ToList();
It gets the right roles, but the Groups and Screens properties are null. Looks like EF has a problem with using Include and second from.
Any help on how to include the properties or rewrite the query will be appreciated.
Eager Loading
The reason for this is that you have specified only one level of include, while your query is asking for something on the second level.
Your include lets you ask for ur.Groups and ur.Screens.
The next level is from g in ur.Groups, and you haven't included that level. (This is probably unexpected for you, since you already have asked for all AccUserRoles in the first part of the query.)
To make your query run, you could add another .include at the start, going two levels deep:
from ur in db.AccUserRoles
.Include("Groups")
.Include("Groups.Roles")
.Include("Screens")
If you need to go yet another level, you'd just add yet another include:
from ur in db.AccUserRoles
.Include("Groups")
.Include("Groups.Roles")
.Include("Groups.Roles.Groups")
.Include("Screens")
...etc.
This might become cumbersome if you have a whole lot of levels to nest, so an alternative would be to use Lazy Loading instead, as Praval 'Shaun' Tirubeni suggests, by adding the virtual keyword to the collections in your entities.
Move the include before ToList().
select ur).Include("Groups").Include("Screens").ToList();
The subselect can remove the Include effect.
If you are doing eager loading, the virtual keyword is not needed.
By adding virtual, you are using lazy loading, not eager loading.
Try adding the virtual key word to your class properties like so:
public class AccUserRole
{
public long Id { get; set; }
public string RoleName { get; set; }
public virtual List<AccAdGroup> Groups { get; set; }
public virtual List<AccScreen> Screens { get; set; }
}
public class AccAdGroup
{
public long Id { get; set; }
public string AdIdent { get; set; }
public virtual List<AccUserRole> Roles { get; set; }
}
public class AccScreen
{
public long Id { get; set; }
public string ScreenIdent { get; set; }
public virtual List<AccUserRole> Roles { get; set; }
}

How can I do an EF Linq query, including a subset of related entities

I have the following classes:
public class Problem
{
public Problem()
{
this.Questions = new HashSet<Question>();
this.Solutions = new HashSet<Solution>();
}
public int ProblemId { get; set; }
public string Title { get; set; }
public string Note { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Solution> Solutions { get; set; }
}
public class Question
{
public int QuestionId { get; set; }
public int ProblemId { get; set; }
public int QuestionStatusId { get; set; }
public string Note { get; set; }
public virtual Problem Problem { get; set; }
}
public class Solution
{
public int SolutionId { get; set; }
public int Number { get; set; }
public int ProblemId { get; set; }
public bool? Correct { get; set; }
public string Text { get; set; }
public string Note { get; set; }
public virtual Problem Problem { get; set; }
}
Can someone help me with LINQ for my EF6,1 SQL Server 2012.
What I would like to do is to get a List that contains only a subset of the data. In this case I would like the Notes properties in Problem, Question and Solution Entities to not be fetched from the database.
Note the Question and Solution tables are connected to the Problem table. I'm not 100% sure of this but I think this means I don't need to add an .Include.
Ideally I would like the selects that EF causes to be issues to not include the Notes column.
You can use table splitting feature of EF. Create a Problem(PK+all fields except for Notes) and a ProblemNotes(PK+Notes) entities. Then querying against Problem should satisfy your needs.
http://msdn.microsoft.com/en-us/data/jj715645.aspx
With Entity Framework table splitting you can separate the properties that might contain very large amount of data into a separate entity and only load it when required.
You might use .Select(...) to make avoid fetching redundantly data from db. The code below illustrates how to fetch list of Problems with only ProblemId and Title fields:
var result = context.Problems.Select(problem => new { ProblemId = problem.ProblemId , Title = proble.Title }).ToList();
Using of .Select above will generate SQL query "SELECT p.ProblemId,p.Title from dbo.Problems as p".
Using of .List will retrieve data (it will not be dependent on context anymore )
Your might cast resulted set to Problem type ,eg:
var newResult = result.Select(x=>new Problem() { ProblemId = x.ProblemId, Title = x.Title } )

Categories