I have a self-referential model class:
public class Word
{
public virtual int Id { get; set; }
public virtual string Text { get; set; }
public virtual IList<Word> Synonyms { get; set; }
public virtual int Extra { get; set; }
}
I am trying to query for all synonyms of a word where Extra is 1 and returning the list of words in JSON format in my MVC 3 app:
[HttpPost]
public JsonResult Synonyms(string wordText)
{
using (var session = ...)
{
using (var tx = session.BeginTransaction())
{
var word = session.QueryOver<Word>()
.Where(w => w.Text == wordText)
.SingleOrDefault();
var results = new SynonymsResults()
{
Words = word.Synonyms
.Where(x => x.Extra == 1)
.Select(x => x.Text)
};
return Json(results);
}
}
}
I'm getting an error that it fails to lazily initialize the collection. I'm not sure why though, since I am still in the same session here and even using a transaction.
The result executes much later, after the action has finished running and outside of the session. The fact that you have returned Json(results) doesn't mean that these results will be immediately serialized into JSON. The action will first finish executing, then the ASP.NET MVC pipeline will handle the execution to the result (OnResultExecuting) and it is at this point that the JavaScriptSerializer will touch the collection. At that point of time sessions and transactions are long gone.
So either instruct your ORM to eagerly fetch the dependent collections or even better take a look at the following series of blog posts and use view models.
To get rid of the error, install Nuget Pacakage Manager Newton.JSON and map to the appropriate project and decorate the property with [JsonIgnore], this will skip the serialization issues and you won't get the error.
Related
In my learning project, while using EF Core, I'm following the repository pattern and I'm using DI to inject the context for each repository.
The context is registered as follows.All services and repositories are transient.
builder.Services.AddDbContext<AppContext>(options => {
options.UseSqlServer(builder.Configuration.GetConnectionString("ConnectionString"));
options.EnableSensitiveDataLogging();
});
I added sensitive data logging as an attempt to debug this but to no avail so far.
The exception appears when I attempt to use:
_context.Update(playableCharacter)
await _context.SaveChangesAsync();
There is a static list I use to store the playableCharacter until all changes are finished and it's ready to be saved in the database again.
Here is my controller:
public async Task<ActionResult> CommitPlayerAction()
{
var userId = _userService.GetUserId(User); //Retrieves user ID from ClaimsPrincipal
var activeGameInstance = await _userParagraphRepository.GetActiveByUserIdNoTrackingAsync(_userService.GetUserId(User)); //Call to repository
// Call to static list storing fight instances. They aren't supposed to be saved in DB.
var activeFightInstance = _fightService.GetActiveFightInstance(userId, activeGameInstance.ActiveCharacter.CharacterId);
await _fightService.CommitAction(userId); // Manage the fight state + Makes call to playable character repository for update. That's where the issue arises.
// Retrieves fight state for given request from the static list
var fightState = _fightService.GetFightState(_userService.GetUserId(User),activeGameInstance.ActiveCharacter.CharacterId);
activeFightInstance.ActionGrantedByItem = false;
_fightService.ResetActiveTarget();
_fightService.ResetActiveAction();
}
The service layer:
public async Task CommitAction(string userId)
{
/*Game logic I cut off for clarity, mostly changes on playable character and his enemies */
var combatEnded = IsFightFinished(_activeFightInstance.ActiveEnemies, GetActivePlayer());
if (combatEnded)
{
var fightWon = IsFightWon(_activeFightInstance.ActivePlayer);
FinishFight(fightWon);
await _playableCharacterRepository.UpdateAsync(_activeFightInstance.ActivePlayer);
}
else
{
// Some game logic
}
}
Service layer dependencies:
private IFightRepository _fightRepository;
private FightInstance _activeFightInstance;
private IFightFactory _fightFactory;
private IUserParagraphRepository _userParagraphRepository;
private ICharacterFactory _characterFactory;
private readonly IPlayableCharacterRepository _playableCharacterRepository;
public FightService(IFightRepository fightRepository,
IFightFactory fightFactory,
IUserParagraphRepository userParagraphRepository,
ICharacterFactory characterFactory,
IPlayableCharacterRepository playableCharacterRepository)
{
_fightRepository = fightRepository;
_fightFactory = fightFactory;
_userParagraphRepository = userParagraphRepository;
_characterFactory = characterFactory;
_playableCharacterRepository = playableCharacterRepository;
}
public FightInstance GetActiveFightInstance(string userId, int characterId)
{
// This fight instance stores a reference to our playable character in the static list to share with the entire service.
_activeFightInstance = _fightRepository.GetById(userId, characterId);
return _activeFightInstance;
}
"Game instance" repository:
public async Task<UserParagraph> GetActiveByUserIdNoTrackingAsync(string userId)
{
return await _context.UserParagraphs
.Include(x => x.ActiveCharacter)
.Include(x => x.Paragraph)
.ThenInclude(p => p.Choices)
.Include(x => x.Paragraph)
.ThenInclude(x => x.TestProp)
.Include(x => x.Paragraph)
.ThenInclude(x => x.FightProp)
.ThenInclude(y => y.ParagraphEnemies)
.ThenInclude(z => z.Enemy)
.Include(x => x.ActiveCharacter)
.AsNoTracking()
.SingleOrDefaultAsync(s => s.User.Id == userId && s.ActiveGame);
}
Fight repository (with the static list that might be causing issues)
internal class FightRepository : IFightRepository
{
// I use a List instead of a dictionary as I need to have non unique records inside
private static List<KeyValuePair<string, FightInstance>> FightInstances { get; set; } = new List<KeyValuePair<string, FightInstance>>();
The entity I'm trying to update:
[Key]
public int CharacterId { get; set; }
public bool IsTemplate { get; set; }
public string UserId { get; set; }//Id of character owner
public User User { get; set; }
public UserParagraph? UserParagraph { get; set; } //Game instance in the form of a many to many relationship between the user and " paragraphs".
public int? UserParagraphId { get; set; } //Nullable as a character can be an instance or a template for other users. It has to remain like this.
public PlayableRace Race { get; set; }
public int RaceId { get; set; }
public PlayableClass CharacterClass { get; set; }
public int PlayableClassId { get; set; }
Many to many
//=============================================================//
//Those entities are causing issues , they might be removed or changed and I won't know about it when updating.
//That's why the "Update" method seemed perfect.
public List<ActionCharacter>? LinkedActions { get; set; }
public List<ItemCharacter>? LinkedItems { get; set; }
//=============================================================//
It's a possible duplicate of this question however after trying everything I can't solve the exception from the title.
What I tried so far:
I've attempted to remove the " as no tracking" and keep a tracked instance in the static List. This lead to EF claiming Character ID already exists as a tracked entity.
Based on the above i tried to use this in my update method in character repository but it leads to exactly the same issue
_db.Entry(playableCharacter).State = EntityState.Detached;
_db.Set<PlayableCharacter>().Update(playableCharacter);
await _db.SaveChangesAsync();
I've made sure all my async calls to DB are awaited to make sure there is no issue from this side.
I've added .AsNoTracking to the request retrieving the playable character before adding it to the static list to make sure EF doesn't have any issues from this perspective.
I've tried using the _context.Entry(PlayableCharacter) methods to force the modified entity state. It worked. That's what worked the best so far. But it forced me to iterate on both many to many lists and I would like to avoid that cause it feels like hacking EF instead of understanding it and working with it.
I've made sure the context change tracker is empty when leaving the .AsNonTracking request for the game instance in the controller. Meaning this might be somehow related to that static list but since the instance in the static list is also non tracking how can that be the issue? If that was the case, using .AsNoTracking() on the request in the controller should be enough for it to work.
I've tried many different approaches but it has been 8 hours now, the last 3 attempts led me to a successful update on all linked entities [many to many collections removal excluded] (while having only _context.PlayableCharacters.Update(playableCharacter) in the repository) and that's the version I posted. But clicking the" browser link" function in visual studio led me to square 1, now EF has issues with PlayableCharacters.LinkedItems.
And this means I completely misunderstood the way EF treats his context instances.
Any help in understanding how exactly EF works with entities in static lists + hopefully getting the update method to work in a reliable way instead of what seems to be now complete randomness is appreciated.
Edit:
Checking the _context.ChangeTracker.Longview prior to using the update method shows there is no tracked object at all(And that's expected).
Still using the update method returns the same exception which means EF is actually tracking this object somehow without telling me where and why.
I expect him to not be tracking anything at all when this update function is called.
Problem is finally solved.
It turns out while requesting one of my objects from EF context I used .Include(x=>x.User) twice. While I heard EF would ignore a duplicated Include it doesn't seem to be the case.
All many to many relations for this user were duplicated( ID included).
Meaning the entity with a given ID was indeed already tracked despite being unique in the DB and everywhere else in the code.
The usage of a static List.
While this seemed smart at first( to limit the amount of requests to DB ) it clearly caused more issues than I thought forcing me to " map" those changes back instead of applying them with one method.
At this point it will be faster to get rid of the static list.
I have the following method which is meant to build me up a single object instance, where its properties are built via recursively calling the same method:
public ChannelObjectModel GetChannelObject(Guid id, Guid crmId)
{
var result = (from channelObject in _channelObjectRepository.Get(x => x.Id == id)
select new ChannelObjectModel
{
Id = channelObject.Id,
Name = channelObject.Name,
ChannelId = channelObject.ChannelId,
ParentObjectId = channelObject.ParentObjectId,
TypeId = channelObject.TypeId,
ChannelObjectType = channelObject.ChannelObjectTypeId.HasValue ? GetChannelObject(channelObject.ChannelObjectTypeId.Value, crmId) : null,
ChannelObjectSearchType = channelObject.ChannelObjectSearchTypeId.HasValue ? GetChannelObject(channelObject.ChannelObjectSearchTypeId.Value, crmId) : null,
ChannelObjectSupportingObject = channelObject.ChannelObjectSupportingObjectId.HasValue ? GetChannelObject(channelObject.ChannelObjectSupportingObjectId.Value, crmId) : null,
Mapping = _channelObjectMappingRepository.Get().Where(mapping => mapping.ChannelObjectId == channelObject.Id && mapping.CrmId == crmId).Select(mapping => new ChannelObjectMappingModel
{
CrmObjectId = mapping.CrmObjectId
}).ToList(),
Fields = _channelObjectRepository.Get().Where(x => x.ParentObjectId == id).Select(field => GetChannelObject(field.Id, crmId)).ToList()
}
);
return result.First();
}
public class ChannelObjectModel
{
public ChannelObjectModel()
{
Mapping = new List<ChannelObjectMappingModel>();
Fields = new List<ChannelObjectModel>();
}
public Guid Id { get; set; }
public Guid ChannelId { get; set; }
public string Name { get; set; }
public List<ChannelObjectMappingModel> Mapping { get; set; }
public int TypeId { get; set; }
public Guid? ParentObjectId { get; set; }
public ChannelObjectModel ParentObject { get; set; }
public List<ChannelObjectModel> Fields { get; set; }
public Guid? ChannelObjectTypeId { get; set; }
public ChannelObjectModel ChannelObjectType { get; set; }
public Guid? ChannelObjectSearchTypeId { get; set; }
public ChannelObjectModel ChannelObjectSearchType { get; set; }
public Guid? ChannelObjectSupportingObjectId { get; set; }
public ChannelObjectModel ChannelObjectSupportingObject { get; set; }
}
this is connecting to a SQL database using Entity Framework Core 2.1.1
Whilst it technically works, it causes loads of database queries to be made - I realise its because of the ToList() and First() etc. calls.
However because of the nature of the object, I can make one huge IQueryable<anonymous> object with a from.... select new {...} and call First on it, but the code was over 300 lines long going just 5 tiers deep in the hierarchy, so I am trying to replace it with something like the code above, which is much cleaner, albeit much slower..
ChannelObjectType, ChannelObjectSearchType, ChannelObjectSupportingObject
Are all ChannelObjectModel instances and Fields is a list of ChannelObjectModel instances.
The query takes about 30 seconds to execute currently, which is far too slow and it is on a small localhost database too, so it will only get worse with a larger number of db records, and generates a lot of database calls when I run it.
The 300+ lines code generates a lot less queries and is reasonably quick, but is obviously horrible, horrible code (which I didn't write!)
Can anyone suggest a way I can recursively build up an object in a similar way to the above method, but drastically cut the number of database calls so it's quicker?
I work with EF6, not Core, but as far as I know, same things apply here.
First of all, move this function to your repository, so that all calls share the DbContext instance.
Secondly, use Include on your DbSet on properties to eager load them:
ctx.DbSet<ChannelObjectModel>()
.Include(x => x.Fields)
.Include(x => x.Mapping)
.Include(x => x.ParentObject)
...
Good practice is to make this a function of context (or extension method) called for example BuildChannelObject() and it should return the IQueryable - just the includes.
Then you can start the recursive part:
public ChannelObjectModel GetChannelObjectModel(Guid id)
{
var set = ctx.BuildChannelObject(); // ctx is this
var channelModel = set.FirstOrDefault(x => x.Id == id); // this loads the first level
LoadRecursive(channelModel, set);
return channelModel;
}
private void LoadRecursive(ChannelObjectModel c, IQueryable<ChannelObjectModel> set)
{
if(c == null)
return; // recursion end condition
c.ParentObject = set.FirstOrDefault(x => x.Id == c?.ParentObject.Id);
// all other properties
LoadRecursive(c.ParentObject, set);
// all other properties
}
If all this code uses the same instance of DbContext, it should be quite fast. If not, you can use another trick:
ctx.DbSet<ChannelObjectModel>().BuildChannelObjectModel().Load();
This loads all objects to memory cache of your DbContext. Unfortunately, it dies with context instance, but it makes those recursive calls much faster, since no database trip is made.
If this is still to slow, you can add AsNoTracking() as last instruction of BuildChannelObjectModel().
If this is still to slow, just implement application wide memory cache of those objects and use that instead of querying database everytime - this works great if your app is a service that can have long startup, but then work fast.
Whole another approach is to enable lazy loading by marking navigation properties as virtual - but remember that returned type will be derived type anonymous proxy, not your original ChannelObjectModel! Also, properties will load only as long you don't dispose the context - after that you get an exception. To load all properties with the context and then return complete object is also a little bit tricky - easiest (but not the best!) way to do it to serialize the object to JSON (remember about circural references) before returning it.
If that does not satisfy you, switch to nHibernate which I hear has application wide cache by default.
What is the correct way to save a graph of objects whose state you don't know? By state I mean whether they are new or existing database entries that are being updated.
For instance, if I have:
public class Person
{
public int Id { get; set; }
public int Name { get; set; }
public virtual ICollection<Automobile> Automobiles { get; set; }
}
public class Automobile
{
public int Id { get; set; }
public int Name { get; set; }
public short Seats { get; set; }
public virtual ICollection<MaintenanceRecord> MaintenanceRecords { get; set ;}
public virtual Person Person { get; set; }
}
public class MaintenanceRecord
{
public int Id { get; set; }
public int AutomobileId { get; set; }
public DateTime DatePerformed { get; set; }
public virtual Automobile Automobile{ get; set; }
}
I'm editing models, similar to these objects above, and then passing those models into the data layer to save, where for this instance I happen to be using entity framework. So I'm translating these models into POCO entities internal to the DAL.
It appears that unless my models have a state indicating whether they are new or updated, I have quite a bit of work to do to "Save" the changes. I have to first select the Person entity, update it, then match any existing Automobiles and update those and add any new, then for each automobile check for any new or updated maintenance records.
Is there a faster/easier way of doing this? It's possible I can keep track of the Model state, which I guess would be helpful with this, but it would mean changes to code outside of the data layer which i would prefer to avoid. I'm just hoping there is a pattern of usage out there that I can follow for updates like this.
I ran into this issue a while back and have been following this thread on the EF Codeplex site. https://entityframework.codeplex.com/workitem/864
Seems like it is being considered for the next release, I'm assuming EF 7, which apparently is a pretty large internal overhaul of EF. This may be worth checking out... http://www.nuget.org/packages/RefactorThis.GraphDiff/
Back when I was working on this I found another EF post on SO, and someone had an example of how to do this manually. At the time I decided to do it manually, not sure why, GraphDiff looks pretty cool. Here is an example of what I did.
public async Task<IHttpActionResult> PutAsync([FromBody] WellEntityModel model)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var kne = TheContext.Companies.First();
var entity = TheModelFactory.Create(model);
entity.DateUpdated = DateTime.Now;
var currentWell = TheContext.Wells.Find(model.Id);
// Update scalar/complex properties of parent
TheContext.Entry(currentWell).CurrentValues.SetValues(entity);
//We don't pass back the company so need to attached the associated company... this is done after mapping the values to ensure its not null.
currentWell.Company = kne;
// Updated geometry - ARGHHH NOOOOOO check on this once in a while for a fix from EF-Team https://entityframework.codeplex.com/workitem/864
var geometryItemsInDb = currentWell.Geometries.ToList();
foreach (var geometryInDb in geometryItemsInDb)
{
// Is the geometry item still there?
var geometry = entity.Geometries.SingleOrDefault(i => i.Id == geometryInDb.Id);
if (geometry != null)
// Yes: Update scalar/complex properties of child
TheContext.Entry(geometryInDb).CurrentValues.SetValues(geometry);
else
// No: Delete it
TheContext.WellGeometryItems.Remove(geometryInDb);
}
foreach (var geometry in entity.Geometries)
{
// Is the child NOT in DB?
if (geometryItemsInDb.All(i => i.Id != geometry.Id))
// Yes: Add it as a new child
currentWell.Geometries.Add(geometry);
}
// Update Surveys
var surveyPointsInDb = currentWell.SurveyPoints.ToList();
foreach (var surveyInDb in surveyPointsInDb)
{
// Is the geometry item still there?
var survey = entity.SurveyPoints.SingleOrDefault(i => i.Id == surveyInDb.Id);
if (survey != null)
// Yes: Update scalar/complex properties of child
TheContext.Entry(surveyInDb).CurrentValues.SetValues(survey);
else
// No: Delete it
TheContext.WellSurveyPoints.Remove(surveyInDb);
}
foreach (var survey in entity.SurveyPoints)
{
// Is the child NOT in DB?
if (surveyPointsInDb.All(i => i.Id != survey.Id))
// Yes: Add it as a new child
currentWell.SurveyPoints.Add(survey);
}
// Update Temperatures - THIS IS A HUGE PAIN = HOPE EF is updated to handle updating disconnected graphs.
var temperaturesInDb = currentWell.Temperatures.ToList();
foreach (var tempInDb in temperaturesInDb)
{
// Is the geometry item still there?
var temperature = entity.Temperatures.SingleOrDefault(i => i.Id == tempInDb.Id);
if (temperature != null)
// Yes: Update scalar/complex properties of child
TheContext.Entry(tempInDb).CurrentValues.SetValues(temperature);
else
// No: Delete it
TheContext.WellTemperaturePoints.Remove(tempInDb);
}
foreach (var temps in entity.Temperatures)
{
// Is the child NOT in DB?
if (surveyPointsInDb.All(i => i.Id != temps.Id))
// Yes: Add it as a new child
currentWell.Temperatures.Add(temps);
}
await TheContext.SaveChangesAsync();
return Ok(model);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
return InternalServerError();
}
This is a huge pain to me too. I extracted the answer from #GetFuzzy to a more reusable method:
public void UpdateCollection<TCollection, TKey>(
DbContext context, IList<TCollection> databaseCollection,
IList<TCollection> detachedCollection,
Func<TCollection, TKey> keySelector) where TCollection: class where TKey: IEquatable<TKey>
{
var databaseCollectionClone = databaseCollection.ToArray();
foreach (var databaseItem in databaseCollectionClone)
{
var detachedItem = detachedCollection.SingleOrDefault(item => keySelector(item).Equals(keySelector(databaseItem)));
if (detachedItem != null)
{
context.Entry(databaseItem).CurrentValues.SetValues(detachedItem);
}
else
{
context.Set<TCollection>().Remove(databaseItem);
}
}
foreach (var detachedItem in detachedCollection)
{
if (databaseCollectionClone.All(item => keySelector(item).Equals(keySelector(detachedItem)) == false))
{
databaseCollection.Add(detachedItem);
}
}
}
With this method in place I can use it like this:
public void UpdateProduct(Product product)
{
...
var databaseProduct = productRepository.GetById(product.Id);
UpdateCollection(context, databaseProduct.Accessories, product.Accessories, productAccessory => productAcccessory.ProductAccessoryId);
UpdateCollection(context, databaseProduct.Categories, product.Categories, productCategory => productCategory.ProductCategoryId);
...
context.SubmitChanges();
}
However when the graph gets deeper, I have a feeling this will not be sufficient.
What your looking for is the Unit of Work pattern:
http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
You can either track UoW on the client and pass it in with the DTO or have the server figure it out. Both the veritable DataSet and EF Entities have their own internal implementation of UoW. For something stand alone there is this framework, but I have never used it so have no feedback:
http://genericunitofworkandrepositories.codeplex.com/
Alternatively another option is to do real time updates with undo functionality, kind of like when you go into Gmail contacts and it saves the changes as you make them with the option to undo.
It depends HOW you are accomplishing adding/changing the entities.
I think you may be trying to do too much with an entity at any given time. Allowing editing and adding at the same time can get you into a situation where your not sure what is being done with the entity, especially in a disconnected scenario. You should only perform a single action on a single entity at a time, unless you are deleting entities. Does this seem monotonous, sure, but 99% of your users want a clean and easily understandable interface. Many time we end up making screens of our applications "god" screens where everything and anything can be done. Which 9/10 times isn't needed (YAGNI).
This way, when you edit a user, you know you are doing an update operation. If you are adding a new maintenance record, you know you are creating a new record that is attached to an automobile.
To summarize, you should limit how many operations you are making available for a single screen and make sure you provide some type of unique information for the entity so you can try to look up the entity to see if it exists.
I had the similar problem, and couldnt find my own solution. I think that problem is complex. Complete solution for updating graphs in disconected scenario with EF6 I find in extension method RefactoringThis.GraphDiff produced by Brent McKendric.
Exemple brings by author is:
using (var context = new TestDbContext())
{
// Update the company and state that the company 'owns' the collection Contacts.
context.UpdateGraph(company, map => map
.OwnedCollection(p => p.Contacts, with => with
.AssociatedCollection(p => p.AdvertisementOptions))
.OwnedCollection(p => p.Addresses)
);
context.SaveChanges();
}
See more at:
http://blog.brentmckendrick.com/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/
I'm new to the world of .Net, ASP, Entity Framework, and Linq, so bear with me...
I originally had a model set up like the following;
public class Pad
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid PadId { get; set; }
public string StreetAddress { get; set; }
public int ZipCode { get; set; }
public virtual ICollection<Mate> Mates { get; set; }
public virtual ICollection<Message> Messages { get; set; }
}
(A pad is a chat room - it contains many Mates and many thousands of Messages)
In a Web API controller, I have a function designed to get the 25 most recent messages from the specified Pad.
public IHttpActionResult GetMessages(string id)
{
var padGuid = new Guid(id);
// Try to find the pad referenced by the passed ID
var pads = (from p in db.Pads
where p.PadId == padGuid
select p);
if (pads.Count() <= 0)
{
return NotFound();
}
var pad = pads.First();
// Grab the last 25 messages in this pad.
// PERFORMANCE PROBLEM
var messages = pad.Messages.OrderBy(c => c.SendTime).Skip(Math.Max(0, pad.Messages.Count() - 25));
var messagesmodel = from m in messages
select m.toViewModel();
return Ok(messagesmodel);
}
The problem with this implementation is that it seems as though EF is loading the entire set of messages (multiple thousands) into memory before getting the count, ordering, etc. Resulting in a massive performance penalty in Pads with a ton of messages.
My first thought was to convert the Pad.Messages member type to an IQueryable instead of an ICollection - this should defer the Linq queries to SQL, or so I thought. Upon doing this, however, functions like pad.Messages.Count() above complain - turns out that pad.Messages is a null value! And it breaks in other places, such as adding new Messages to the Pad.Messages value.
What is the proper implementation of something like this? In other places, I've seen the recommended solution is constructing a second query against the context such as select Messages where PadId = n, but this hardly seems intuitive when I can have a Messages member value to work with.
Thank you!
var messages = db.Pads.Where(p => p.PadId == padGuid)
.SelectMany(pad => p.Messages.OrderBy(c => c.SendTime)
.Skip(Math.Max(0, pad.Messages.Count() - 25)));
How do you plan to count the number of results in a DB query without actually executing the DB query?
How do you plan to get the first item in the query without actually executing the query?
You cannot do either; both must execute the query.
I am using Entity Framework CodeFirst where I have used Parent Child relations using ICollection as
public class Person
{
public string UserName { get;set}
public ICollection<Blog> Blogs { get; set;}
}
public class Blog
{
public int id { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
Ok, so far everything is working ok, but my concern is, whenever I want to get the Blogs of a person, I get it as
var thePerson = _context.Persons.Where(x => x.UserName = 'xxx').SingleOrDefault();
var theBlogs = thePerson.Blogs.OrderBy(id).Take(5);
Now, I understand that, when the line is executed, all Blogs for that person is loaded into the memory and then sorting and selecting is done from memory. That is not ideal for a record of Person who has large number of blogs. I want to make the Blog Child as IQueryable so that the Sorting and Selecting is done in SQL database before pulling to Memory.
I know I could declare the Blogs as IQueryable in my context so that I could directly query as
var theBlogs = _context.Blogs.Where(.....)
but that is not feasible for me due to design choice, I want to avoid any circular reference as much as possible due to serialization problem. So, I did not make any reference of the parent entity in my child.
I found that, i can call AsQueryable() method on the blogs as
var theBlogs = thePerson.Blogs.AsQueryable().OrderBy(id).Take(5);
That looks like a magic for me and seems too good to be true. So my question. Does this AsQueryable really make the ICollection as IQueryable in reality and makes all Query process in SQL Server (Lazy loading) OR it is just a casting where Blogs are loaded into memory as like before, but change the interface from ICollection to IQueryable ?
So actually it appears that writing your navigation property as IQueryable<T> is not possible.
What you could do is adding a navigation property to Blog:
public class Blog
{
public int id { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public virtual Person Owner { get; set; }
}
From that, you can query as follows so it won't load everything into memory:
var thePerson = _context.Persons.Where(x => x.UserName = 'xxx').SingleOrDefault();
var results = _context.Blogs.Where(z => z.Person.Name = thePerson.Name).OrderBy(id).Take(5)
I suggest you to try LINQPad to see how LINQ is translated into SQL, and what is actually requested from the DB.
A better approach is described in Ladislav's answer. In your case:
var theBlogs = _context.Entry(thePerson)
.Collection(x => x.Blogs)
.Query()
.OrderBy(x => x.id)
.Take(5);