I'm building an Orchard CMS module, where I want to eager load data, but can't work out how to do this.
For example a Client has many Events, so I have a ClientRecord & EventRecord for these:
public class ClientRecord {
private IList<EventRecord> _eventRecords;
public virtual int Id { get; set; }
public virtual string Company { get; set; }
public virtual IList<EventRecord> EventRecords {
get { return _eventRecords ?? (_eventRecords = new List<EventRecord>()); }
set { _eventRecords = value; }
}
}
WhenI load a Client in my ClientController
var clientRecord = _clientRepository.Get(id);
and then display the Events in my View
<ul>
#foreach (var eventRecord in Model.EventRecords) {
<li>
#eventRecord.Name (#eventRecord.StartDate.ToShortDateString())
</li>
}
</ul>
the Events are displayed and MiniProfiler shows a separate query to lazy-load the Events.
I've tried putting an [Aggregate] attribute on the EventRecords collection in the ClientRecord, but this didn't have any effect.
I'm not that familiar with NHibernate, so hopefully this is something simple, but how do I specify I want to eager load the EventRecords when the ClientRecord is retrieved?
[EDIT]
In Orchard CMS the NHibernate mappings are created for you, based on the convention that the class is called xxxRecord and there is a database table in place with the same name.
So you don't (as far as I know) have a mapping file that you can specify this in. If I'm right about that, then the question is whether there is any way to specify you want eager loading in the query you use to retrieve the Client (rather like Entity Framework's "include" method).
You must specify eager loading in your nHibernate mapping file, using Fluent nhibernate this would look (something) like this:
HasMany(x => x.EventRecords).KeyColumn("CompanyId").Not.LazyLoad().Fetch.Select();
The Fetch.Select() will execute a second select statement to load all of the related Event records.
The Not.LazyLoad() tells nHibernate to execute this second select immediately, if you remove this execution would be deferred until the collection is accessed.
In response to your comments you can specify eager loading in your query using fetch also (LINQ example shown)
NHSession.Query<ClientRecord>().Fetch(c => c.EventRecords).ToList();
Related
ASP.NET Core 2 Web application using a REST API. Currently using sqlite3 for development database. (Also tried migrating to SQL Server and got same results as below).
I'm sending an entity to web client, the client makes changes to the entity that involve adding a new related entity and then that updated principle entity gets sent back as json in body of PUT a request.
I was hoping the new related entity would get created automatically, but this is not happening. The simple properties on the principle entity are updated properly, but not reference properties. I'm not getting any exceptions or anything - it just seems to be ignoring the reference properties.
Simplified Classes (I removed other properties that shouldn't affect the relationship):
public partial class DashboardItem {
public int Id { get; set; }
public int? DataObjectId { get; set; }
public DataObject DataObject { get; set; }
}
public partial class DataObject {
public int Id { get; set; }
}
Portion of DbContext Fluent API for associated property:
modelBuilder.Entity<DashboardItem>(entity => {
entity.HasOne(p => p.DataObject)
.WithMany()
.HasForeignKey(p => p.DataObjectId);
});
Controller Method for PUT:
[HttpPut("{id}")]
public async Task<IActionResult> PutDashboardItem([FromRoute] int id, [FromBody] DashboardItem entity)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != entity.Id)
{
return BadRequest();
}
_context.Entry(entity).State = EntityState.Modified;
try{
await _context.SaveChangesAsync();
}catch (DbUpdateConcurrencyException)
{
if (!DashboardItemExists(id)){
return NotFound();
}else {
throw;
}
}
return NoContent();
}
The simplified json (without all the other properties) would look like this (I've tried different variations of have the foreign key "DataObjectId" removed from the json, set to null, or set to zero in case that might be interferring.):
{
Id:1,
DataObjectId:null,
DataObject:{
Id: 0
}
}
When debugging in the controller action method, the existing "DashboardItem" principle entity created from the request body has the reference property "DataObject" populated before getting added to the DbContext, but the new DataObject never gets created in the database. There is only a SQL UPDATE statement issued for DashboardItem and no INSERT for DataObject.
I've also tried making the controller method synchronous instead of async, using DbContext.SaveChanges() instead of .SaveChangesAsync(), since there used to be a problem with that in earlier versions of EF Core related to creating related entities, even though I'm using 2.0 which already has a fix for that. Same result.
This EFCore Doc sounds like it should just work out of the box.
This has worked for me in a prior project. What am I missing here?
Basically, my mistake was in assuming the process of updating data was much simpler than it actually is when sending the updated data from a client in a web application.
After digging a lot more, it seems that the following line in my controller method for handling the PUT request is the problem:
_context.Entry(entity).State = EntityState.Modified;
Setting the entity entry state to Modified in this way results in Entity Framework Core ignoring the reference properties for the related objects - the SQL UPDATE generated will only address the columns in the entity table.
This simple summary eventually got me started down the right path.
Summarizing what I've now learned:
This controller method is dealing with a 'detached' entity that was edited and sent back from the client. The DbContext is not yet tracking this entity since I get a new instance of the context with each http request (hence the entity is considered 'detached'). Because it is not being tracked yet, when it is added to the DbContext, the context needs to be told whether this entity has been changed and how to treat it.
There are several ways to tell the DbContext how to handle the detached entity. Among those:
(1) setting the entity state to EntityState.Modified will result in ALL properties being included in the SQL update (whether they've actually changed or not), EXCEPT for the reference properties for related entities:
_context.Entry(entity).State = EntityState.Modified;
(2) adding the entity with a call to DbContext.Update will do the same as above, but will include the reference properties, also include ALL properties on those entities in the update, whether they've changed or not:
_context.Update(entity)
Approach #2 got things working for me, where I was just trying to get the new related child entity to be created in the Update to its parent.
Beyond that, DbContext.Attach() and DbContext.TrackGraph sound like thy provide more find-grained control over specifying what specific properties or related entities to include in the update.
When I have an entity object with a one-to-many child collection, and I need to query for a specific child object, is there a feature or some clever pattern I haven't come up with yet to avoid that NHibernate fetches the entire child collection?
Example:
class Parent
{
public virtual int Id { get; proteced set; } // generated PK
public virtual IEnumerable<Child> Children { get; proteced set; }
}
class Child
{
public virtual int Id { get; protected set; } // generated PK
public virtual string Name { get; protected set; }
public virtual Parent Parent { get; protected set; }
}
// mapped with Fluent
class Service
{
private readonly ISessionFactory sessionFactory;
public Service(ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
void DoSomethingWithChildrenNamedBob(int parentId)
{
using(var session = sessionFactory.OpenSession())
{
var parent = session.Get<Parent>(parentId);
// Will cause lazy fetch of all children!
var childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
// do something with the children
}
}
}
I know it's not the best example because in this case one would probably just query the Child entities directly, but I have encountered situations where I already had a Parent object and needed to traverse specific sub-trees through it.
Short answer: no. Longer answer: you can make it do this, with some sleight of hand.
Rippo's answer above shows how you would do it the 'proper' NHibernate way (whether it's with Linq or QueryOver or HQL doesn't really matter - the point is you have to step outside the parent -> child relationship to do a query). You can take this a step further and disguise this behind a façade. But to do so, you have to remove the mapped relationship entirely and replace it with a query at all times. You'd take out the Parent -> Children mapping, but leave the Child -> Parent mapping intact; then re-write the property on Parent to look like this:
public virtual IQueryable<Child> Children
{
get
{
// somehow get a reference to the ISession (I use ambient context), then
return session.Query<Child>().Where(c => c.Parent == this);
}
}
Now, when you use Parent.Children you get back a queryable collection, so you could then write
IEnumerable<Child> childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
The only way you could do this and preserve the mapping is to amend NHibernate's collection objects (or inject your own). Diego Mijelshon (who is around these parts) wrote a spike of exactly that, adding IQueryable support to NHibernate collections so you could do
IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");
But from what I can see, this never went any further and there's no apparent plan to add this capability to NH. I have run Diego's code and it does work, but obviously it's not production quality and hasn't been tested, and I don't think it's ever been officially 'released' even as a private patch.
Here's the link to the discussion on the NH issue tracker: https://nhibernate.jira.com/browse/NH-2319
I believe NH should support this out of the box, as it's a natural way for most .NET devs to want to interact with pretty much anything enumerable, now that we have Linq, and not being able to do it without the side-effect of loading an unbounded collection into RAM sucks. But the traditional NH model is session -> query and that's what 99% of people use.
I asked the same question on NHusers a few weeks ago and didn't get an answer so I suspect the answer is you will always get all the parents children and then perform a in-memory filter. In many cases this might be the correct way in seeing it.
In your case I would rewrite the query to be:-
var childrenNamedBob = session.Query<Children>()
.Where(w => w.Parent.Id == parentId && w.Name == "Bob");
Then simply to get parent (if childrenNamedBob has results) you could call:-
var parent = childrenNamedBob.First().Parent;
or as you rightly pointed out:-
var parent = session.Get<Parent>(parentId);
You can now do that with NHibernate 5 directly without specific code !
See https://github.com/nhibernate/nhibernate-core/blob/master/releasenotes.txt
Build 5.0.0
=============================
** Highlights
...
* Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...
I've grown accustomed in SQLAlchemy (Python) to map a relationship/collection with lazy="dynamic" which maps the property as a Query object instead of a populated list/collection (or Proxy for lazy loaded properties). This mapped property then allows you to further refine the query used to fetch the collection before doing so (apply an order, limit, filter, etc).
For example, in SQLAlchemy I can map a relationship like so:
class Post(Base):
...
class User(Base):
...
posts = relationship(Post, lazy="dynamic")
And then when I retrieve a user, I can apply an order on posts, or only retrieve the last 5, etc.
user = session.query(User).get(1)
# Fetch the last 5 posts by user 1
posts = user.posts.order_by(Post.create_date.desc()).limit(5).all()
http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html
I would love to find a way to do this using Fluent NHibernate, mapping the collection as a QueryOver or IQueryable (LINQ) such as:
public virtual QueryOver<Post> Posts {get; set;}
or
public virtual IQueryable<Post> Posts { get; set; }
and in the mappings do something like:
public class UserMap : ClassMap<User>
{
public UserMap()
{
...
HasMany(u => u.Posts).Fetch.Dynamic
}
}
Is this currently possible using Fluent NHibernate (or just NHibernate)?
It is possible using NHibernate, however it's not quite out of the box.
You would need to write your own collectionwrapper implementing IQueryable, inject it with your own CollectionFactory and delegate the query generation to the session which loaded the containing object.
Folks, I know I didn't phrase that title very well, but here's the scenario.
I have a WinForm UI tier, and a WCF middle tier, serving up my EF4 entity objects, which are (of course) mapped to my database tables. Everything works fine.
One of my objects is the Client - and in the Client db table are three varbinary(max) fields for PDF documents. So my entity object has three Byte() properties, one for each document.
But when I load up an initial grid listing the Clients, it's going to drag ALL that PDF data from the MT - making a much bigger payload than I generally need.
With DataSets, I'd write my SQL to not include the PDF binary - but I'd include a Boolean flag field for each to indicate whether there IS one to download if the user wants it. Then I'd load the PDFs via a separate call as needed.
With EF4 - what's the best pattern for this?
First, I'm thinking to put the documents into a child-table/child-objects, so I don't pull it across the tier with the Client. One problem solved.
Second, I suppose I could use partial classes to extend my Client entity object to have the three Boolean properties I want.
Am I on the right track?
I think you have three options:
1) Create a custom class which you project the properties you want into:
public class MySpecialSelection
{
public int ID { get; set; }
public string Name { get; set; }
// more
public bool HasPDFDoc1 { get; set; }
public bool HasPDFDoc2 { get; set; }
public bool HasPDFDoc3 { get; set; }
}
using (var context = new MyContext())
{
var mySpecialSelectionList = context.MyEntities.Where(...some predicate...)
.Select(e => new MySpecialSelection
{
ID = e.ID,
Name = e.Name,
// ...
HasPdfDoc1 = (e.PdfDoc1 != null),
HasPdfDoc2 = (e.PdfDoc2 != null),
HasPdfDoc3 = (e.PdfDoc3 != null),
}).ToList();
// ...
}
Instead of a "named" object you can also project into anonymous types.
Note: This doesn't attach any full model entity to the context, so you won't have any change tracking of entities.
2) Table splitting: It means that you split your single entity into two separate classes which are related by a navigation property. You can map then both entities to a single table in the database. It allows you to load the navigation properties (for instance the binary fields) on request (by lazy, eager or explicite loading). Details about this for EF4.0 are here and for EF4.1 here.
3) Your own proposal: Create separate tables and separate entities which are linked by navigation properties and FK constraints.
I have a table logging web page hits. Something like: {VisitId, VisitorId, Url, Date}
(The visitor ID is a GUID stored in a cookie)
I would like to create a Visitor object that has a collection of Visit objects.
class Visitor {
public virtual Guid VisitorId { get; set; }
public virtual IList<Visit> Visits { get; set; }
}
Rather than add another table for Visitor, can NHibernate create this object just from the collection of Visits?
Ideally, I would like to write:
var visitor = session.Get<Visitor>(guidFromCookie)
And then be able to work with the Visits list and persist changes back to the DB.
(I'm using FluentNHibernate and NHibernate 3.0)
I'm new to NHibernate, but it seems the something should be possible using a custom IEntityPersister, or is this too low level and loads of work? Any suggestions would be appreciated.
When you say "create this object", do you mean retrieve? What is your reason for not having a visitor table? You could use the criteria API or hbm to load a list of visits by the guid if you don't want a visitor entity/table.
If you mapped Visitor and made it Lazy Loaded, you might be able to do this. You'd have to tell NHibernate that the table existed, even though it didn't. However, when you want to get the Visitor object (note that the only property mapped is the Id), then instead of using .Get(), use .Load() which will return an uninitialized proxy. So you'll have an entity, but it won't actually hit the database, so it will never know that the table doesn't exist.
public class VisitorMap : ClassMap<Visitor>
{
public VisitorMap()
{
Table("SomeNonExistentTable");
LazyLoad(); // should be the default anyway
Id(x => x.Id)
.GeneratedBy.Guid();
HasMany(x => x.Visits)
.AsList()
.Not.LazyLoad();
}
}
...and then...
var visitor = session.Load<Visitor>(guidFromCookie);
foreach(var visit in visitor.Visits)
{
// do wonderful things
}