Entity Framework ObjectQuery.Include() - c#

I have an object with two objects as properties (User, PrimaryNode), both could potentially be null, see below:
public class Item
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public Node PrimaryNode { get; set; }
public User User { get; set; }
}
I'm using Entity Framework 6 to populate the Item object and using chained includes to populate the PrimaryNode and User objects within it.
When the first chained Include has a null object then the whole object returns as null, for example:
using (var db = new MyContext())
{
var item = db.Items.Include(i => i.User).Include(n => n.PrimaryNode).FirstOrDefault(i => i.ItemId == id);
}
If in the above example i.User is null then the item variable is null. Whats the best way of populating both the sub-objects in a way that if a sub-object is null then the parent object and the other sub-object will still be populated?

I don't think your issue is due to the Include calls. According with the documentation:
This extension method calls the Include(String) method of the
IQueryable source object, if such a method exists. If the source
IQueryable does not have a matching method, then this method does
nothing.
In other words is going to be translated to:
var item = db.Items.Include("User").Include("PrimaryNode").FirstOrDefault(i => i.ItemId == id);
My question is, are you sure you have an Item with that id properly related with existing rows in Users and PrimaryNodes tables in your DB?. When you call Include method at the end is going to be translated to a join, so if the FK of your relationship doesn't match with the PK that reference, your query should not return what you are expecting.
Anyways, if you want to try another variant to load related properties you can use Explicit Loading:
var item = db.Items.FirstOrDefault(i => i.ItemId == id);
context.Entry(item).Reference(p => p.PrimaryNode).Load();
context.Entry(item).Reference(p => p.User).Load();

I think it would be better if you use Lazy loading int his situation. Just make the User and PrimaryNode virtual:
public class Item
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public virtual Node PrimaryNode { get; set; }
public virtual User User { get; set; }
}
And then:
var db = new MyContext();
var item = db.Items.FirstOrDefault(i => i.ItemId == id);

As others have mentioned, I think your issue is not due to the Includes. However, I think the following method has value. It is functionally equivalent to what you are already doing with the chained includes, but I think it has several benefits including making the intention of the code clear to the user.
The includes can be placed in Extension methods:
using System.Data.Entity;
using System.Linq;
namespace Stackoverflow
{
public static class EntityExtensions
{
public static IQueryable<Item> IncludePrimaryNode(this IQueryable<Item> query)
{
// eager loading if this extension method is used
return query.Include(item => item.PrimaryNode);
}
public static IQueryable<Item> IncludeUser(this IQueryable<Item> query)
{
// eager loading if this extension method is used
return query.Include(item => item.User);
}
}
}
Then, you can use the extensions as follows:
using (var db = new MyContext())
{
var itemQuery = db.Items.IncludeUser();
itemQuery = itemQuery.IncludePrimaryNode();
var item = itemQuery.FirstOrDefault(i => i.Id == 1);
}
It's just another way of doing the same thing, but I like the clarity it adds to the code.

Related

.net Linq command doesn't return certain fields

I'm having an issue where objects are coming back as null even if they passed linq tests and I can see the values in the db, and I am stuck unsure where to go to fix this. I'm not normally a c# developer so this is new territory for me.
My table looks like
Class Meeting {
...
public virtual List<MeetingParticipant> Participants { get; set; }
...
}
Class MeetingParticipant {
public bool isOrganiser { get; set; }
public Account Participant { get; set; }
public ParticipatingState ParticipatingState { get; set; }
public string responseText { get; set; }
}
the only bind i have is: modelBuilder.Entity<Meeting>().OwnsMany(meeting => meeting.Participants);
and my linq command is:
var meetings = (from m in _context.Meetings
where m.Participants.Any(val => val.Participant.PhoneNumber == passedInPhoneNumber && val.ParticipatingState == ParticipatingState.Pending)
select m);
Annoyingly when I dig into the meetup objects that are returned, there is participants however their Account object is null. However, for the meetup to pass the linq request, it had to exist so I could compare its phone number.
What am I missing?
A simple adjustment to your Linq command should get you the results you want:
var meetings = from m in _context.Meetings.Include(val => val.Participant)
where m.Participants.Any(val => val.Participant.PhoneNumber == passedInPhoneNumber && val.ParticipatingState == ParticipatingState.Pending)
select m;
The .Include(val => val.Participant) is the magic here - it tells EF to "eagerly" load and populate that entity in your results.
Learn more about eager loading here: https://www.entityframeworktutorial.net/eager-loading-in-entity-framework.aspx
Edit: As mentioned in Beau's comment, for this to work, you need to add the following using statement:
using System.Data.Entity;

Sorting by Children in Entity Framework doesn't return sorted list

I know that Entity framework doesn't support sort or filter in the children collections yet. What I thought is that first I get the data then use foreach loop to sort it. The result gives me an unsorted list. My goal is to get the Participants (any order) and CurrentHospitaliztions (order by id descending) which is a child of participants. The models and the query is below. Any help will be appriciated.
public class Participant
{
public int Id { get; set; }
.. other fields
public ICollection<CurrentHospitalization> CurrentHospitalizations { get; set; }
public Participant()
{
CurrentHospitalizations = new Collection<CurrentHospitalization>();
}
}
public class CurrentHospitalization
{
public int Id { get; set; }
.. other fields
public Participant Participant { get; set; }
public int ParticipantId { get; set; }
}
The query that I use:
public async Task<IEnumerable<Participant>> GetList()
{
var participants = await context.Participants
.Include(x => x.CurrentHospitalizations)
.ToListAsync();
foreach (var p in participants )
{
var s = p.CurrentHospitalizations;
foreach (var q in s)
{
s.OrderByDescending(u => u.Id);
}
}
return participants ;
}
You sorted the right piece, in the wrong place, and then didn't really do anything with it. You don't need the nested iteration, you can just do it from the single foreach loop like this:
foreach (var p in participants)
p.CurrentHospitalizations = p.CurrentHospitalizations.OrderByDescending( ch => ch.Id ).ToList();
What you ahve here is a failure to understand the basics of LINQ and not reading the manual.
var s = p.CurrentHospitalizations;
foreach (var q in s)
{
s.OrderByDescending(u => u.Id);
}
does effectively nothing else than waste processor time.
You make a variable s. You assign to it the current hospuzatlizations, not sorted.
Then you call OrderByDescending - generating an expression that you COULD execute, except you never DO execute it. So, you created some object tree and - throw it away.
o.CurrentHospitalizations = s.OrderByDescending(u => u.Id).ToList()
would assign s to be a list and execute it. The ToList() is missing - as well as the assigning it, so the sorted result is not just thrown away.
Those are LINQ basics - orderby etc. are not changing order, they return an ordered result, and you must materialize it.

Sorting Multiple Levels of Child Collections with LINQ to Entities

Consider the following model:
public class Form
{
public Guid Id
{
get;
set;
}
public List<Section> Sections
{
get;
set;
}
}
public class Section
{
public Guid Id
{
get;
set;
}
public List<Question> Questions
{
get;
set;
}
public int SortOrder
{
get;
set;
}
}
public class Question
{
public Guid Id
{
get;
set;
}
public int SortOrder
{
get;
set;
}
}
When I retrieve one or more Form objects using LINQ to Entities, I would like to have the associated collection of Section objects sorted by the SortOrder property. Further, within each of these Section objects, I would like to have the associated collection of Question objects sorted in the same fashion.
I don't recall where I read it, but I have been able to get the first-level sort to work using a LINQ query similar to the following:
var query =
from
f in context.Form
where
f.Id == *some form id*
select
new
{
Root = f,
Sections = f.Sections.OrderBy(s => s.SortOrder)
};
From there, I could get the actual Form object by using something like:
var form = query.ToList().Select(q => q.Root).FirstOrDefault();
What I cannot figure out is how to write the LINQ query to extend this behavior down to the second-level collection (the collection of Question objects within each Section object.
* UPDATE *
See my comment to Ivan below which explains how this question is not a duplicate.
To achieve what you need you can use Eager Loading:
var query= from f in context.Form.Include(f=>f.Sections.Select(s=>s.Questions))
where f.Id == *some form id*
select
new
{
Root = f,
Sections = f.Sections
.OrderBy(s => s.SortOrder)
.Select(s=> new{s.Id, s.SortOrder, Questions=s.Questions.OrderBy(q=>q.SortOrder)})
};
Include extension method allows you include as part of your query related entities, even deep levels (check the Remarks section in the link I quoted above).
A second solution could be using Lazy Loading, if you haven't disabled this feature, which is enable by default, you need to meet some requirements to be used, for example, your navigation properties must be virtual.
Update
You can also sort your navigation properties in memory like the solution in the post that was quoted by #IvanStoev, but if you want to bring the related entities in some order, filtered, among other operations, you could consider use Explicit Loading:
foreach f in context.Form
{
context.Entry(f).Collection(r => r.Sections)
.Query().OrderBy(s=>s.SortOrder)
.Load();
}
But IMHO the best solution is create custom classes (also called DTOs) to project the desired result, to load only the data that you need in one round trip
Having in mind you are directly querying the whole set of tables, then you have no need for Eager Loading with the method .Include().
Here is a lambda expression way of solving this with explictly mapping properties/columns.
// TODO: replace the null value with a real context DbSet<Form>
IQueryable<Form> forms = null;
var form = forms.Select(x => new Form()
{
Id = x.Id,
Sections = x.Sections.OrderBy(s => s.SortOrder).Select(s => new Section()
{
Id = s.Id,
SortOrder = s.SortOrder,
Questions = s.Questions.OrderBy(q => q.SortOrder).Select(q => new Question()
{
Id = q.Id,
SortOrder = q.SortOrder
}).ToList()
}).ToList()
}).FirstOrDefault();

Entity Framework 6.1: navigation properties not loading

This is my first time using Entity Framework 6.1 (code first). I keep running into a problem where my navigation properties are null when I don't expect them to be. I've enabled lazy loading.
My entity looks like this:
public class Ask
{
public Ask()
{
this.quantity = -1;
this.price = -1;
}
public int id { get; set; }
public int quantity { get; set; }
public float price { get; set; }
public int sellerId { get; set; }
public virtual User seller { get; set; }
public int itemId { get; set; }
public virtual Item item { get; set; }
}
It has the following mapper:
class AskMapper : EntityTypeConfiguration<Ask>
{
public AskMapper()
{
this.ToTable("Asks");
this.HasKey(a => a.id);
this.Property(a => a.id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(a => a.id).IsRequired();
this.Property(a => a.quantity).IsRequired();
this.Property(a => a.price).IsRequired();
this.Property(a => a.sellerId).IsRequired();
this.HasRequired(a => a.seller).WithMany(u => u.asks).HasForeignKey(a => a.sellerId).WillCascadeOnDelete(true);
this.Property(a => a.itemId).IsRequired();
this.HasRequired(a => a.item).WithMany(i => i.asks).HasForeignKey(a => a.itemId).WillCascadeOnDelete(true);
}
}
Specifically, the problem is that I have an Ask object with a correctly set itemId (which does correspond to an Item in the database), but the navigation property item is null, and as a result I end up getting a NullReferenceException. The exception is thrown in the code below, when I try to access a.item.name:
List<Ask> asks = repo.GetAsksBySeller(userId).ToList();
List<ReducedAsk> reducedAsks = new List<ReducedAsk>();
foreach (Ask a in asks)
{
ReducedAsk r = new ReducedAsk() { id = a.id, sellerName = a.seller.username, itemId = a.itemId, itemName = a.item.name, price = a.price, quantity = a.quantity };
reducedAsks.Add(r);
}
Confusingly, the seller navigation property is working fine there, and I can't find anything I've done differently in the 'User' entity, nor in its mapper.
I have a test which recreates this, but it passes without any problems:
public void canGetAsk()
{
int quantity = 2;
int price = 10;
//add a seller
User seller = new User() { username = "ted" };
Assert.IsNotNull(seller);
int sellerId = repo.InsertUser(seller);
Assert.AreNotEqual(-1, sellerId);
//add an item
Item item = new Item() { name = "fanta" };
Assert.IsNotNull(item);
int itemId = repo.InsertItem(item);
Assert.AreNotEqual(-1, itemId);
bool success = repo.AddInventory(sellerId, itemId, quantity);
Assert.AreNotEqual(-1, success);
//add an ask
int askId = repo.InsertAsk(new Ask() { sellerId = sellerId, itemId = itemId, quantity = quantity, price = price });
Assert.AreNotEqual(-1, askId);
//retrieve the ask
Ask ask = repo.GetAsk(askId);
Assert.IsNotNull(ask);
//check the ask info
Assert.AreEqual(quantity, ask.quantity);
Assert.AreEqual(price, ask.price);
Assert.AreEqual(sellerId, ask.sellerId);
Assert.AreEqual(sellerId, ask.seller.id);
Assert.AreEqual(itemId, ask.itemId);
Assert.AreEqual(itemId, ask.item.id);
Assert.AreEqual("fanta", ask.item.name);
}
Any help would be extremely appreciated; this has been driving me crazy for days.
EDIT:
The database is SQL Server 2014.
At the moment, I have one shared context, instantiated the level above this (my repository layer for the db). Should I be instantiating a new context for each method? Or instantiating one at the lowest possible level (i.e. for every db access)? For example:
public IQueryable<Ask> GetAsksBySeller(int sellerId)
{
using (MarketContext _ctx = new MarketContext())
{
return _ctx.Asks.Where(s => s.seller.id == sellerId).AsQueryable();
}
}
Some of my methods invoke others in the repo layer. Would it better for each method to take a context, which it can then pass to any methods it calls?
public IQueryable<Transaction> GetTransactionsByUser(MarketContext _ctx, int userId)
{
IQueryable<Transaction> buyTransactions = GetTransactionsByBuyer(_ctx, userId);
IQueryable<Transaction> sellTransactions = GetTransactionsBySeller(_ctx, userId);
return buyTransactions.Concat(sellTransactions);
}
Then I could just instantiate a new context whenever I call anything from the repo layer: repo.GetTransactionsByUser(new MarketContext(), userId);
Again, thanks for the help. I'm new to this, and don't know which approach would be best.
Try to add
Include call in your repository call:
public IQueryable<Ask> GetAsksBySeller(int sellerId)
{
using (MarketContext _ctx = new MarketContext())
{
return _ctx.Asks
.Include("seller")
.Include("item")
.Where(s => s.seller.id == sellerId).AsQueryable();
}
}
Also, there is an extension method Include which accepts lambda expression as parameter and provides you type checks on compile time
http://msdn.microsoft.com/en-us/data/jj574232.aspx
As for the context lifespan, your repositories should share one context per request if this is a web application. Else it's a bit more arbitrary, but it should be something like a context per use case or service call.
So the pattern would be: create a context, pass it to the repositories involved in the call, do the task, and dispose the context. The context can be seen as your unit of work, so no matter how many repositories are involved, in the end one SaveChanges() should normally be enough to commit all changes.
I can't tell if this will solve the lazy loading issue, because from what I see I can't explain why it doesn't occur.
But although if I were in your shoes I'd like to get to the bottom of it, lazy loading is something that should not be relied on too much. Take a look at your (abridged) code:
foreach (Ask a in asks)
{
ReducedAsk r = new ReducedAsk()
{
sellerName = a.seller.username,
itemName = a.item.name
};
If lazy loading would work as expected, this would execute two queries against the database for each iteration of the loop. Of course, that's highly inefficient. That's why using Include (as in Anton's answer) is better anyhow, not only to circumvent your issue.
A further optimization is to do the projection (i.e. the new {) in the query itself:
var reducedAsks = repo.GetAsksBySeller(userId)
.Select(a => new ReducedAsk() { ... })
.ToList();
(Assuming – and requiring – that repo.GetAsksBySeller returns IQueryable).
Now only the data necessary to create ReducedAsk will be fetched from the database and it prevents materialization of entities that you're not using anyway and relatively expensive processes as change tracking and relationship fixup.

Reference remains null

I think i ran into a bug, it seems that EF is not handling references well after deleting and reinserting an entity. I've managed to reproduce it with the code below (assume all asserts pass except the one i talk about in the comments):
var database = new TestEntities();
// select and delete the info record
var info = database.Info.First(i => i.ID == 1);
Assert.AreEqual(1, info.MemberID);
// when i uncomment the line below the last Assert fails
// Assert.IsNotNull(info.Member);
database.Info.Remove(info);
// add it again and persist it to the database
database.Info.Add(new Info {
ID = 1,
MemberID = 1
});
database.SaveChanges();
// should not be null ? EDIT: i guess i understand this becoming null
Assert.IsNull(info.Member);
// and even here its still null
info = database.Info.First(i => i.ID == 1);
Assert.IsNull(info.Member);
Can anyone tell me whats going on here?
EDIT:
My entities are generated using database first and im using the DbContext/POCO generator.
public partial class Member
{
public Member()
{
this.Info = new HashSet<Info>();
}
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Info> Info { get; set; }
}
public partial class Info
{
public int ID { get; set; }
public int MemberID { get; set; }
public virtual Member Member { get; set; }
}
It turns out that it had nothing to do with deleting and reinserting, not really anyway. It was was so obvious ...
I was inserting using a POCO which is not eagerly loaded and does not have any lazy loading capabilities ...
The second time i queried for the same record i was expecting a proxy, but it seems that the POCO was cached by EF and that is what it returned meaning still no eager or lazy loading.
I can fix it by making sure EF doesn't retrieve the second query from cache, inserting using a proxy (var info = database.Info.Create()) or including member in the query (database.Info.Include(i => i.Member).First(i => i == 1)).
Given this
var info = database.Info.First(i => i.ID == 1);
Assert.AreEqual(1, info.MemberID);
Aren't you comparing info.ID to info.MemberID here? Isn't it possible that ID and MemberID actually are different?
Also, shouldn't you be using .SaveChanges() after
database.Info.Remove(info);
?
Also, info does not have .member available if none have been instantiated. Is there a correlating Member with MemberID equal to info.MemberId?

Categories

Resources