nhibernate can you lazyload without proxies? - c#

I've started digging into Nhibernate and although there are many things I do like, there is one ting I dislike: the "generate proxy"/lazy load mechanism. The idea that I have to maintain some sort of reference to the ISession and make sure the entities are associated with the session before accessing a property that might trigger lazy-loading is a bit more plumbing in my viewmodels than I appreciate. In the last ORM mapper I used, we had a different approach to lazy loading which enabled used to disregard the session problem entirely at the cost of less POCO entities. I basically want to do the same thing with nhibernate, with some syntax similar to this:
public class Order
{
// this will introduced through the ctor using for ex an interceptor and Castle Windsor
private IOrmService ormService;
List<OrderLine> details = new List<OrderLine>();
public IEnumerable<OrderLine> Details
{
get
{
ormService.LazyLoad(this, o => o.Details);
return this.details;
}
}
}
Where the idea is that the ormService will simply disregard the lazy load request from the entity if the collection has already been loaded (nevermind the state management issue :-)). I looked into the NHibernateUtils-class, which has some Initialized and Initialize-collection methods, but they assume that you are using proxies. Basically, i need a way of telling nhibernate someting like: "hey, populate this property using this session which i'm giving you". The state-management, etc can be handled externally. Is there support for doing this in Nhibernate?

I'm a bit confused with your problem. I feel like following Hibernate best practices should be enough, no need to re-invent what already there. Here are a few comments.
The idea that I have to maintain some
sort of reference to the ISession and
make sure the entities are associated
with the session before accessing a
property that might trigger
lazy-loading is a bit more plumbing in
my viewmodels than I appreciate
If you follow the open session in view pattern, this becomes really easy.
Where the idea is that the ormService
will simply disregard the lazy load
request from the entity if the
collection has already been loaded
That's what the lazy proxy does already.
Basically, i need a way of telling
nhibernate someting like: "hey,
populate this property using this
session which i'm giving you".
That what you do when you detach/attach objects to a session. The lazy items will be loaded according to the session the object is attached to.

The answer is no you cannot have lazy loading without proxies. Either the proxies need to be created by NHibernate or any type of class or pattern you implement will produce the same end result except you'll just have moved around where/how the proxies are generated.
For the lazy loading to work, it inherently needs the ISession otherwise there would be no way for it to connect to the database to retrieve the needed values.
The way NH has implemented its proxies pattern is probably the best you will ever achieve atleast until .NET 4.0 the new dynamic word might shake things up a bit.

Related

Entity Framework - Efficient eager loading with long chains of dependent objects?

I have an issue, which I assume many professional developers will run into. My workplace has adopted entity framework. We use it, and love it. However, we seem to have run into a very frustrating limitation.
Let's assume you have an object chain as such
A -> B -> C -> D
We are professionals, so these objects have a lot of data, and there is a lot of them in their respective database tables. It seems EF has a terrible time loading anything past object B. The SQL queries it generates are really inefficient and not good. The call would be something like
context.objects.include("bObjectName.cObjectName.dObjectName").FirstOrDefault(x => x.PK == somePK);
We have gotten around this by explicitly loading objects past that second level with the .Load() command. This works well for single objects. However, when we talk about a collection of objects, we start to run into issues with .Load().
Firstly, there does not seems to be a way to keep proxy tracking of objects in a collection without the virtual keyword. This makes sense because it needs to overwrite the get and set functions. However, this enables lazy loading, and .Load() doesn't map entities when lazy loading is enabled. I find this to be somewhat odd myself. If you remove the virtual keyword, .Load() does automatically link loaded objects to the relevant objects in context.
So here is the crux of my issue. I want proxy tracking, but also want .Load() to map the navigation properties for me. None of this would be an issue if EF could generate good queries. I understand why it can't, it has to be a one show fits all kind of thing.
So to load the third tier of objects I might create a loader function in my service layer that takes all the primary keys of the second tier of objects, and then calls a .Load() on them.
Does anyone have a solution for this? It seems like EF7, or Core 1.0 solves this by:
Removing lazy loading entirely, which we could shut off as well, but it would break a lot of older code.
Adding a new "ThenInclude" feature, which supposedly increases the efficiency of chaining includes massively.
If turning off lazy loading is the answer, that's fine, I just want to exhaust all options before I waste a lot of time redeveloping a huge webapps worth of service calls.
Does anyone have any ideas? I'm willing to give anything a shot. We are using EF6.
EDIT: It seems the answer is to shut off lazy loading at a context level, or upgrade to EF7. I'll change this if anyone else manages to find a solution whereby you can have proxy tracking with forced eager loading on a single object for EF6.
You're absolutely right about chained .Include() statements, the performance documentation warns against chaining more than 3 as each .Include() adds an outer join or union. I didn't know about ThenInclude but it sounds like a gamechanger!
If you keep your virtual navigation properties but turn off Lazy Loading on the DbContext
context.ObjectContext().ContextOptions.LazyLoadingEnabled = false;
then (as long as Change Tracking is enabled) you can do the following:
var a = context.aObjectNames.First(x=> x.id == whatever);
context.bObjectNames.Where(x=> x.aId == a.Id).Load()
This should populate a.bObjects

How to create a Navigation Property that returns IQueryable<T> in Entity Framework

My project uses Entity Framework 6 with Repository & UnitOfWork patterns to communicate with a MSSQL database. all configurations are done with Code First.
All of the navigation properties of the Entity Objects are defined for lazy loading as such:
public virtual ICollection<> Items { get; set; }
this works well, and the performance is pretty good in most cases. in some cases, however, where the navigation properties refer to a large sets of data, loading them to memory takes a while, at which point LINQing seem like a bad practice. to improve performance, it would make sense to convert some of them to IQueryable<> but EF doesn't seem to accept that kind of configuration.
So although i could workaround this by "Querying" the repositories, in some cases i already have an entity object in memory and it feels right to be able to query its navigation properties without loading all of them, to get the data that i need via LINQ.
Is there a way to make this work?
You do not. This functionality is not available in Entity Framework. YOu can make a feature request. But right now - no way.

Using EntityFramework navigation properties when the DbContext is disposed

The current retrieval pattern in my Service classes (in an ASP.NET MVC application) looks something like:
public Client Get(int id)
{
using (var repo = _repoFactory.Get<Client>())
{
return repo.Get(id);
}
}
Where _repoFactory.Get<T>() returns a repository which, when disposed, also disposes the Entity Framework DbContext;
However, when the consumer of the Get(int id) method needs to use navigation properties on the Client object, an exception is thrown because the context is already disposed.
I can foresee a few ways to negotiate this problem:
Don't use navigation properties outside of the service
Don't use lazy-loading navigation properties
Find some other way to dispose of the context when the request is finished
What is the "correct" (or least incorrect) way and how can it be accomplished?
All the ways that you suggested are "correct," and each has its advantages and disadvantages. You'll need to decide which approach you want to use.
Don't use navigation properties outside of the service
This is difficult to enforce if the service is returning entities. In my current project, we make heavy use of "DTO"s, which are new classes that represent the data that we expect to need in a given context. Because these aren't entities, we know that any property on them will be fully hydrated before it is returned from the repository.
Don't use lazy-loading navigation properties
This is roughly the same as above, except that you're allowing for the possibility of certain navigation properties to be eager-loaded. Again, how does the developer consuming this data know which properties are and are not going to be available? "DTO"s solve this problem, but they also introduce a bunch of extra classes that are almost identical to the existing entities.
Find some other way to dispose of the context when the request is finished
Usually people do this by having contexts bound in a per-request scope in their DI framework, and allow the DI framework to take care of instantiation/disposal of their contexts.
The main danger with this approach is that, while lazy-loading properties won't throw exceptions when accessed, each access requires another database round-trip. This makes it easy for developers to accidentally write code that ends up making thousands of round-trips when only two or three would be required otherwise.
However, if you have a reliable way of identifying performance issues and addressing them, then you could use this approach in the general case and then add a little eager-loading where you find it to be necessary. For example, MiniProfiler can sit on your front-end and give you information about the database round-trips you're making, as well as warnings when it notices that many database queries are practically identical.

Intercepting NHibernate Lazy-Load behaviour to return null if not connected to a session?

This seems like it should be an obvious thing but I've been searching for the answer for hours now with no success.
I'm using NHibernate to persist a domain model, with a service layer that serves an ASP.NET MVC front end (the 'service layer' is currently just a standard class library but may be converted to WCF in the future). The web app asks for the data it wants and specifies the collections on the domain object that it needs, the service layer takes the request, loads the object and required collections (using lazy loading) and passes the object back where it is transformed using AutoMapper to a viewmodel friendly representation.
What I want to be able to do is load the required collections, detach the object from the session and pass it to the front end. However, when AutoMapper tries to map the object this causes a an exception because it's trying to access collections that haven't been initialized and the session is no longer available. I can leave the object connected but in this case the AutoMapper transformation ends up causing all the properties on the object to be lazy-loaded anyway and this won't be an option is we go down the WCF route.
What I want to do is alter this behaviour so that instead of throwing an exception, the collection returns null (or better yet empty) when it is not connected to a session. This was the default behaviour in Entity Framework V1 (which admittedly didn't do auto lazy loading), which I worked with previously but I can't find a way to do it in NH.
Any ideas? Am I on the wrong track here?
EDIT- To be a bit clearer on what I'm trying to achieve, when accessing a collection property I want this behaviour:
Connected to session: lazy-load collection as normal.
No session: property is null (rather than throw exception)
UPDATE - Following this post by Billy McCafferty, I've managed to implement a solution using IUserCollectionType that seems to work so far. Rather than use the provided PersistentGenericBag as he does though, I had to create new types that changed the behaviour when not connected to the session. It's not perfect and requires some very ugly mappings but at least I don't need to touch my domain objects or client mappings to get it working.
The most appropriate solution in this case is probably to check in AutoMapper for lazy-loadable fields if they were indeed loaded with NHibernateUtil.IsInitialized(). Not sure how/if possible to make Automapper use this check for all implicit property mappings though.
Old question but this is what we did to solve the same issue, hopefully it helps to set you on correct path if somebody stumbles upon this problem.

the nhibernate cross machine lazy load problem

i got a client a server and a db
db mapped to server with fluent nhibernate.
so far so good
the client talks with the server with wcf.
i want to implement lazy loading on client side.
is there any solution out there?
AFAIK there is no solution for that, lazy loading works with nhibernate's proxy mechanism which is very intrinsic to its implementation.
there was one project called Nhibernate.Remote but this has been abandoned.
nhibernate would work over wcf, but without lazy loading.
The nature of WCF would be to learn towards your data being shaped prior to sending, ie., it's already been retreived and put into a datacontract of some sort.
Possibly you could provide a parameter on the inital service call to indicate whether child properties should be populated, although how far you go is always going to be tricky.
when i realized that it can't happen, i decided to work with the projection and the result transformer.
it works good.
if i need to load some lazy loading collection i just iterate it on server and send it back to client,
when i wanna update a partially selected object i have to reselect him from db and update the full data entity.
but it's worth it
I'm sorry to be pessimistic but making NHibernate work with lazy loading over WCF is NOT GOING TO WORK.
With the below code WCF can work with lazy loading:
var proxy = myObj as INHibernateProxy;
myObj = (myObj)proxy.HibernateLazyInitializer.GetImplementation();

Categories

Resources