Entity Framework v5 loading relational data - c#

I have two very simple POCO that i want to connect through a one to many relation
public class Menu
{
public int MenuId { get; set; }
public bool IsActive { get; set; }
public ICollection<MenuMember> MenuMembers { get; set; }
}
public class MenuMember
{
public int MenuMemberId { get; set; }
public int MenuId { get; set; }
public string ViewRoute { get; set; }
public bool IsActive{ get; set; }
}
public class EFDbContext : DbContext
{
public DbSet<Page> Pages { get; set; }
public DbSet<Menu > Menus { get; set; }
public DbSet<MenuMember> MenuMembers{ get; set; }
}
Now what I have to do is very simple , but I all the resources on the internet are suprisingly so vague (or i am too dumb)
I want to write an lambda expression for
SELECT *
FROM Menu INNER JOIN MenuMembers
ON Menu.MenuId = MenuMembers.MenuId
WHERE Menu.MenuId = 1
I have used this
IEnumerable<Menu> menu
= repository.Menus.Where(x => x.MenuId == menuId);
but when I iterate over it, menu.MenuNumbers stays null. I believe it is some sort of lazyloading issue.

Either Include() the relation manually for eager loading:
Entity Framework Loading Related Entities:
Eager loading is the process whereby a query for one type of entity also loads related entities as part of the query. Eager loading is achieved by use of the Include method.
IEnumerable<Menu> menu = repository.Menus
.Include(m => m.MenuMembers)
.Where(x => x.MenuId == menuId);
Or mark the property as virtual so Entity Framework will lazy-load it:
Lazy loading is the process whereby an entity or collection of entities is automatically loaded from the database the first time that a property referring to the entity/entities is accessed. When using POCO entity types, lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook.
public class Menu
{
public int MenuId { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<MenuMember> MenuMembers { get; set; }
}
And there's a few other options, be sure to check out the documentation.

You need to declare MenuMembers as virtual
public virtual ICollection<MenuMember> MenuMembers { get; set; }

Related

Entity framework core load navigation properties for recursive table

I am using Entity Framework Core 5.0 and the following model:
So a Job has a MainFlow, and the mainflow can have 0 or more Subflows which in turn can have subflows (recursive)
The way this has been setup is that I have a Job entity which has a MainflowId property (and also navigation property)
The Flows have a ParentFlowId property, and a navigation property with a collection of SubFlows.
I also included a TreeId property on Job and Flow to easily identify a tree.
public class Job
{
public int Id { get; set; }
public DateTimeOffset Timestamp {get; set; }
public string JobName{ get; set; }
public int MainFlowId { get; set; }
public Guid TreeId { get; set; }
public virtual Flow MainFlow { get; set; }
}
public class Flow
{
public int Id { get;set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public Guid TreeId { get; set; }
public int? ParentFlowId { get; set; }
public virtual Flow ParentFlow { get; set; }
public virtual ICollection<Flow> SubFlows { get; set; } = new List<Flow>();
}
What I am trying to achieve is to load a list of jobs (for example based upon the timestamp), but in a way that all the navigation properties become available (Job->Mainflow->Subflow->Subflow->...)
I was able to get this behavior by loading the jobs, and then loading the flows separately using the TreeId, however because of performance issues I am now using .AsNoTracking() and this does not seem to work anymore.
Is there a way to load the whole trees with their navigation properties while using .AsNoTracking?
Thanks for any insight!
Edit:
Here is the way it is working without the AsNoTracking()
(I simplified the code a bit)
private IQueryable<Job> GetAllJobs()
{
return DbContext.Set<Job>()
.Include(a=>a.MainFlow)
}
private IEnumerable<Flow> GetAllFlowsForTreeIds(IEnumerable<Guid> treeIds)
{
var result = from flow in DbContext.Set<Flow>()
.Include(a => a.ParentFlow)
.AsEnumerable()
join treeId in treeIds
on flow.TreeId equals treeId
select flow;
return result;
}
public IEnumerable GetJobTrees()
{
var allJobs = GetAllJobs().ToList();
var flows = GetAllFlowsForTreeIds(allJobs.Select(a=>a.TreeId)).ToList());
//by getting the flows, the navigation properties in alljobs become available
}

Entity Framework .Include throwing NullReferenceException

I have a very basic EF setup that is throwing an odd error when trying to populate a navigation property by using .Include. Here are the entity Models:
public class LineGroup
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public ICollection<LineGroupMember> LineGroupMembers { get; set; }
}
public class LineGroupMember
{
public int ID { get; set; }
public int Extension { get; set; }
public string Name { get; set; }
public int Permissions { get; set; }
public bool IsLoggedIn { get; set; }
public int LineGroupID { get; set; }
internal LineGroup LineGroup { get; set; }
}
I am using these through an injected DB context, and can query each just fine without using navigation properties. I can also query the LineGroups and include the LineGroupMembers property just fine, like so:
var LineGroups = _context.LineGroups.Include(l => l.LineGroupMembers).ToList();
This load all of the line groups into a list that has a correctly working "LineGroupMembers" collection for each Line Group. However, if I try
var lineGroupMembers = _context.LineGroupMembers.Include(m => m.LineGroup).ToList();
I get "NullReferenceException" with no helpful details. Any ideas why the navigation property will work one way and not the other? There are no null values in either database table...
Make your navigation property public
public LineGroup LineGroup { get; set; }
If it is internal it won't be picked up by default by EF. You could also add explicit fluent mapping to force EF to recognize it as well.

Entity Framework model issue

I am having some strange issues, I am attempting to pull a record out of the database and it seems like most of it is null even know if I manually look in the DB it's populated.
Model
public class AdminConfiguration : Entity // Entity is an abstract class containing an ID
{
public bool Authentication { get; set; }
public List<ApplicationConfiguration> ApplicationConfiguration { get; set; }
public List<LinksConfiguration> LinksConfiguration { get; set; }
public EmailConfiguration EmailConfiguration { get; set; }
public bool WakeOnLan { get; set; }
}
Basically any reference to another class is null The only thing that is populated is the WakeOnLan property.
Query
public AdminConfiguration Find(int id)
{
return Db.AdminConfiguration.Find(id);
}
I have a feeling I have a misunderstanding regarding how I set up the models. I am expecting the query to return me a fully populated AdminConfiguration object.
Try to set navigation properties as virtual to enable lazy loading:
public virtual List<ApplicationConfiguration> ApplicationConfiguration { get; set; }
Please refer to https://msdn.microsoft.com/en-us/data/jj193542.aspx
This enables the Lazy Loading feature of Entity Framework. Lazy
Loading means that the contents of these properties will be
automatically loaded from the database when you try to access them.
The best way to setup your model is:
public class AdminConfiguration : Entity // Entity is an abstract class containing an ID
{
public AdminConfiguration()
{
this.ApplicationConfigurations = new HashSet<ApplicationConfiguration>();
this.LinksConfigurations = new HashSet<LinksConfiguration>();
}
public bool Authentication { get; set; }
public EmailConfiguration EmailConfiguration { get; set; }
public bool WakeOnLan { get; set; }
// Navigation properties
public virtual ICollection<ApplicationConfiguration> ApplicationConfigurations { get; set; }
public virtual ICollection<LinksConfiguration> LinksConfigurations { get; set; }
}

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; }
}

Lazy loading not working when adding detached entity

I have an ASP.NET MVC 4 application using Entity Framework 5 under .NET 4.5. The problem I'm having is that when I insert a detached entity that was created on the front-end, the lazy loading is not working.
Here is my code to add (or update):
public static int PersistMergeEntity(EntityTwo entityTwo)
{
int entityId;
using (var _db = new EntityDb())
{
if (_db.EntityTwo.Any(e => e.EntityTwoId == entityTwo.EntityTwoId))
{
_db.Entry(entityTwo).State = EntityState.Modified;
}
else
{
_db.EntityTwo.Add(entityTwo);
}
_db.SaveChanges();
//_db.Entry(entityTwo).Reference(e => e.EntityOne).Load();
entityId = entityTwo.EntityOne.EntityId;
}
EntityBo.UpdateData(entityId);
return entityTwo.EntityTwoId;
}
Here are my entities:
public class EntityTwo
{
[Key]
[ForeignKey("EntityOne")]
public int EntityTwoId { get; set; }
public Decimal NbValue { get; set; }
public virtual EntityOne EntityOne { get; set; }
}
public class EntityOne
{
[Key]
[ForeignKey("EntityTwo")]
public int EntityOneId { get; set; }
[ForeignKey("Entity")]
public int EntityId { get; set; }
public CsMonthDomain CsMonth { get; set; }
public int NbYear { get; set; }
public Decimal NbValue { get; set; }
public virtual Entity Entity { get; set; }
public virtual EntityTwo EntityTwo { get; set; }
}
And Entity is another entity that I need to do calculation every time I update or add EntityTwo.
The code works when the commented line is uncommented. But if it is the way shown up there, lazy loading will not work and I'll get a null Exception.
Lazy loading is set to true and the entities are, supposedly, correct, since it works when I explicitly load the navigation property.
I'm sorry about the names, but unfortunately I cannot post the real code ;(
Lazy loading does not work because the entityTwo you pass into the method is (most likely) not a dynamic proxy which it has to be in order to make lazy loading work. The instance is probably created outside the method using entityTwo = new EntityTwo();. To create a proxy of an entity you would need a context instance available and then use
entityTwo = _db.EntityTwos.Create();
In my opinion using explicit loading (your commented line) is the best solution in this situation. It has the same costs of querying the database once per navigation property like lazy loading would have plus the additional benefit over lazy loading that you could project a selection of properties you only need from the related entity, for example:
entityId = _db.Entry(entityTwo).Reference(eTwo => eTwo.EntityOne).Query()
.Select(eOne => eOne.EntityId)
.Single();

Categories