Ayende @ Rahien

Refunds available at head office

Why Defer Loading in Entity Framework isn’t going to work

When I finished reading this post I let out a heavy sigh. It is not going to work. Basically, the EF is going the same way that NHibernate was in NHibernate 1.0 (circa 2005!).

Let me show you how. in the post, the example given is:

public class Category
{
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
    public virtual List<Product> Products { get; set; }
    ...
}

This looks like it would work, right? The way the EF is doing this is by creating a proxy of your class (similar to the way that NHibernate is doing that) and intercepting the call to get_Products.

It has a few problems, however. Let us start from the most obvious one, program to interface not to implementation. Exposing List<T> means that you have no control whatsoever about what is going on with the collection. It also means that they have to intercept the access to the property, not using the collection. That is bad, it means that they are going to have to do eager loading in all too many cases where NHibernate can just ignore it.

Let us more on a bit, and talk about the inability to support any interesting scenario. If we look at collections with NHibernate, we can take this:

Console.WriteLine(category.Products.Count);

To this SQL:

select count(*) from Products
where CategoryId = 1

With the approach that the EF uses, they will have to load the entire collection.

But all of those are just optimizations, not the make-or-break for the feature. Here is a common scenario that is going to break it:

public class Category
{
    private List<Product> _products;
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
    public virtual List<Product> Products { get { return _products; } set { _products = value; } }

    public void AddProduct(Product p)
    {
        // do something interesting here
        _products.Add(p);
    }
    ...
}

There is a reason why the default proxies for NHibernate force all members of the entities to be virtual. It is not just because we think everything should be virtual (it should, but that is not a discussion for now). It is all about being able to allow the user to use a real POCO.

In the scenario outlined above, what do you think is going to happen?

AddProduct is a non virtual method call, so it cannot be intercepted. Accessing the _products field also cannot be intercepted.

The end result is a NullReferenceException that will seem to appear randomly, based on whatever something else touched the Property or not. And however nice auto properties are, there are plenty of reason to use fields. Using auto properties just mask the problem, but it is still there.

Oh, and if we want to use our POCO class with EF, forget about the new operator, you have to use:

Category category = context.CreateObject<Category>();

Hm, this just breaks just about anything that relies on creating new classes. Want to use your entity as parameter for ASP.Net MVC action, to be automatically bounded? Forget about it. You have to create your instances where you have access to the context, and that is a database specific thing, not something that you want to have all over the place.

And I really liked this as well:

The standard POCO entities we have talked about until now rely on snapshot based change tracking – i.e. the Entity Framework will maintain snapshots of before values and relationships of the entities so that they can be compared with current values later during Save. However, this comparison is relatively expensive when compared to the way change tracking works with EntityObject based entities.

Hm, this is how NHibernate and Hibernate have always worked. Somehow, I don’t see this showing up as a problem very often.

Final thought, why is the EF calling it Defer Loading? Everyone else call it lazy loading.

Comments

Greg Young
05/29/2009 12:11 AM by
Greg Young

Oren, it surprises you that yet another thing is being given a new name in redmond?

configurator
05/29/2009 12:35 AM by
configurator

Careful, they might read your blog and actually learn something...

configurator
05/29/2009 12:38 AM by
configurator

Oh, and Defer Loading is because they wanted to keep it inline with them calling all lazy execution deferred execution...

Dmitry
05/29/2009 01:45 AM by
Dmitry

I am evaluating the Entity Framework 4.0 and want to say that the blog is not entirely correct.

I use POCOs in the project. Company entity has a set of Contact entities that are declared as follows:

public class Company

{

// other properties

private readonly ISet m_contacts = new HashSet();

public virtual ISet Contacts { get; private set; }

}

The deferred loading is enabled and the returned Company object is a proxy. I created an AddContact non-virtual method and it works just fine. You can use any interface that inherits from ICollection as long as you use a backing variable. Collection properties with setters are a bad thing in my opinion.

I agree with your point lazy loading the actual collection but I use IQueryable for that.

Also, you don't have to use the factory method to create entities. The context has an AddObject/DeleteObject/AttachTo/DetachTo methods that take a POCO and return a proxy, if necessary.

I created a IUnitOfWork interface and a context wrapper, and only expose the POCO specific methods.

Dmitry
05/29/2009 01:47 AM by
Dmitry

The property should have been

public virtual ISet Contacts { get { m_contacts } }

James Newton-King
05/29/2009 05:18 AM by
James Newton-King

"Final thought, why is the EF calling it Defer Loading? Everyone else call it lazy loading."

Probably someone in marketing decided defer was a better word than lazy in a feature bullet point.

Fabio Maulo
05/29/2009 05:28 AM by
Fabio Maulo

@Dmitry

We have analyzed that post together. The blog is the blog of "ADO.NET team blog"; who wrote that post is "Faisal Mohamood

Program Manager, Entity Framework".

If what you said "blog is not entirely correct" is true, the problem is another.

So far I said, in various public place, that NH is the reality and EF is the future... since few days, following that blog, I'm begun say that: NH is the reality and EF is a hope.

Mikael Henriksson
05/29/2009 05:29 AM by
Mikael Henriksson

Don't know where anyone get's having to use context.CreateObject <user() from. I am using new User() successfully.

Dmitry
05/29/2009 05:50 AM by
Dmitry

@Fabio,

I don't think anyone argues that Hibernate/NHibernate is a more mature and proven solution.

However, EF v4.0 has been really improved since v1.0. It can truly support POCOs if you know the API. It's extremely easy to turn on code generation and provide your own POCO classes. I has a much better LINQ implementation than v1.0 (and definitely NHibnerate). You can mix and match LINQ and eSQL queries since they both can return IQueryable. Complex/value objects are easily supported. There is a great stored procedure/UDF support where you map them directly to CLR methods. I've been testing it for a couple of weeks an can definitely see myself using it in real life unlike v1.0.

Fabio Maulo
05/29/2009 06:04 AM by
Fabio Maulo

@Dmitry

I hope so, believe me that hope EF will public something usable.

Btw I have some certainty that the concrete collection List <t can't work properly because you have only a chance to intercept the call (the getter in the collection owner) and initialize the collection.

I don't understand where they are implementing UoW and where they are holding the previous entity state and the collection snapshot...

I hope we'll see something soon.

Dmitry
05/29/2009 06:15 AM by
Dmitry

@Fabio,

There is a requirement that only collections that implement ICollection are supported. They are probably just casting it to that interface and only use the interface methods.

The context object (the unit-of-work) contains an EntityStateManager instance that is responsible for holding previous and current entity states and collections/relations.

Ayende Rahien
05/29/2009 06:27 AM by
Ayende Rahien

Dmitry,

You are explicitly initializing the field. Try not doing so, then resolve a instance from the DB and try doing that. You'll see the error.

Fabio Maulo
05/29/2009 06:42 AM by
Fabio Maulo
@Dmitry
  
mmmm List
<t is a concrete class I would understand how they can intercept and Add or a Remove... btw no problem, we will see how the lazy-loading and the collection state is working, soon.
>
Frans Bouma
05/29/2009 07:44 AM by
Frans Bouma

@ Mikael Henriksson

Your object then is just an instance of your entity class and lazy loading won't work.

It's sad that they (the EF team) didn't force MS to create class loaders instead so we could get rid of proxies and simply augment class IL at load time.

Peter Morris
05/29/2009 08:27 AM by
Peter Morris

Can't you give a better example than this?

public void AddProduct(Product p)

{

    _products.Add(p);

}

I wouldn't do that anyway, I would do this

public class Category

{

public virtual List

<product Products { get; private set; }

public Category()

{

    //I'd need to do this in NH anyway for

    //object that were created rather than

    //dehydrated

    Products = new List

<product();

}


public void AddProduct(Product p)

{

    Products.Add(p);

}

}

The only difference I see in the code above and what I would expect to write in NH is that AddProduct is not virtual, which in this example I don't think it needs to be. Now HERE is an example of why you should need to make everything public or protected virtual :-)

public class UserActionLog

{

public string UserName { get; private set; }

private List

<useraction UserActions { get; set; }

public UserActionLog(string userName)

{

    UserName = userName;

    UserActions = new List

<useraction();

}


public IEnumerable

<useraction GetUserActions()

{

    foreach(UserAction ua in UserActions)

        yield ua;

}


public void AddUserAction(UserAction ua)

{

    UserActions.Add(ua);

}

}

The list of UserActions is private so cannot be virtual.

The only way to read the list is via GetUserActions().

The only way to add to the list is via AddUserAction().

So if you access UserActionLog via a proxy, like so

ThisMonthsActions.UserActions["Peter Morris"]

and get a proxy, when you use GetUserActions or AddUserAction there is no way for the proxy to override the access to the private UserActions property. As a consequence you would either need to

A: Eager fetch UserActionLog.UserActions when the state to UserAction (UserName) is fetched, or

B: Make all public members virtual - which is why NH does it this way.

At first I didn't understand why NH requires me to make everything public virtual. At least by enforcing this restriction NH prevents people from accidentally introducing this NullReferenceException bug.

Regards

Pete

Tom Pester
05/29/2009 09:42 AM by
Tom Pester

Its good that you offer constructive crticism. I hope that the MS team will evaluate this and take action if needed.

If you want to stay ahead of the MS team on all fronts than a Linq provider for Nhibernate should be releasd ( yesterday ).

As you stated, not using a mapper is stealing from your clients.

I discovered the same statement to be true for linq. Not using linq is stealing from your client.

Its easy to criticies I know.

So when will we have a production ready linq provider ? I hope you will not avoid this valid question from the community and provide a clear standpoint. The last thing I heard about it was that a full time developer should be hired to program this and there was no budget.

What is the status now?

Krzysztof Kozmic
05/29/2009 09:55 AM by
Krzysztof Kozmic

@Tom,

The nice thing about NHibernate is that, contrary to EF, it's an Open Source project, so if you want a Lnq Provider, do step up and implement it yourself instead of whining and demanding.

With regard to the status of the implementation, Steve Strong did a tremendous job with it, and underlying AST parser so it should be done for the next major version (v3.0).

firefly
05/29/2009 09:59 AM by
firefly

Kozmic, That was nicely put. From the comments on this blog I get a feeling there are a few people and/or companies that want a linq provider for NHibernate yet isn't willing to do anything about it but whining and demanding.

Dan
05/29/2009 10:48 AM by
Dan

Not using linq is stealing from your client.

I can't believe that shiny new LINQ has become the deal-breaker for so many developers when selecting an ORM...

Krzysztof Kozmic
05/29/2009 11:10 AM by
Krzysztof Kozmic

@Dan,

I don't really believe it's a deal breaker. It's more like a shiny new toy that suddenly every kind on the block wants to play with - not a feature with strong technical advantage over Criteria or HQL. I'm using Criteria on my new project and I'm ok with that, although I'd love more lambda driven strong typing (a la Fluent NHibernate).

And Tom, if you so desperately want LINQ in NHibernate and are not willing to help with it yourself, you can help with your wallet.

If you're willing to sponsor the work, I'd be happy to go and implement that for you; this or any other feature that is in line with where the project is heading.

Tom Pester
05/29/2009 11:38 AM by
Tom Pester

Hello Dan,

If I want to continue using nHibernate, which is an excellent and mature product, I want to have a clear statement about future plans so I can adjust my own strategy. I am looking forward to trying out V3 when it is releasaed.

Linq is a wonderful way to query data and it has many applications that make using it essential now that we have it. The smalltalk folks will probably be playing with this for decades.

Minimizing the importance of linq is a mistake. Not using it is "stealing from your client" if I may use this phrase from Ayande. Linq introduces a new way of thinking, for example

blogs.gaiaware.net/.../...ee-programmatically.aspx

On the request to chip in I have no problem with that at all. I can chip in a few 10's. Where can I do this and what is the current/target ammount to be collected? What will happen concretly with this money?

Paying myself for a full linq provider is out of my reach. I also don't think its wise if you get a top notch implementation from MS for free (free as in the framework, not the whole MS server stack one has to purchase). In my opinion MS is lagging behind (always and normal for a big company) but once they release something its stable and bugfree. I prefere workarounds to bugs but thats me.

nHibernate does not have the advangate anymore that it had for years. Now that MS is releasing a mapper that is finaly a respectable attempt you guys should not sit still. Enough whining :)

Frans Bouma
05/29/2009 12:28 PM by
Frans Bouma

"And Tom, if you so desperately want LINQ in NHibernate and are not willing to help with it yourself, you can help with your wallet.

If you're willing to sponsor the work, I'd be happy to go and implement that for you; this or any other feature that is in line with where the project is heading."

No offence to you, Krzysztof, but that will require a very thick wallet and for you a loooooooooooong time away from anything that requires you to step away from your keyboard.

It will be interesting what Steve is able to fabricate, though it's unrealistic (no matter how good Steve is) to expect anything truly solid after a few months work.

Krzysztof Kozmic
05/29/2009 12:46 PM by
Krzysztof Kozmic

Frans,

I'm aware of the amount of work implementing feature-complete-cover-all-corner-cases linq provider requres, and I've been following your discussion with Ayende and other people on this very topic in comments to several posts over the last year or so.

My point is, that NHibernate is a community driven project, and no-one gets paid for working on it, so it's not fair to demand features that are so big undertaking. if someone wants them faster, than they need to provide resources for that - either their own code, or funds for someone else to be able to dedicate some more time to that.

And of course the amount of that support is a linear function of the size of the task, which in this case is quite big.

With regard to Steve's work, I think you can take a look at the current progress in the uNHAddins repository

pete w
05/29/2009 12:53 PM by
pete w

LINQ-based queries havent been implemented in NHibernate, but EF and Linq to SQL are not databse agnostic.

When the project calls for a simplistic DTO model that can be written in an hour with strongly typed datasets, then writing a mapper such as NHibernate IS indeed stealing from your clients.

The important thing is to learn which scenarios one mapper presents advantage over the others.

EF gets you a fast and flexible data model quickly with code gens, but is limited to SQL server, plus it is subject to change.

NHibernate is mature and it gives you the highest degree of control over database interactivity, and it is largely database agnostic, but it comes with a learning curve for the uninitiated.

In terms of standard database interactivity there are numerous viable alternatives to consider (ibatis, linq to sql, castle activerecord, llblgen etc..)

zvolkov
05/29/2009 12:54 PM by
zvolkov

We can nitpick on details of Ayende's post or flame about subtleties of EF vs. NH but the fact that NH is more mature is undeniable. Now, whether EF will grow to displace NH is something we shall live and see.

Dan
05/29/2009 02:04 PM by
Dan

Tom

The blog post you pointed to is entitled "Traversing the ASP.NET Control Tree with LINQ". What does this have to do with a linq provider that is used to retrieve objects that are mapped to tables in a SQL database?

I agree that the features that make linq possible are awesome. Using linq against in-memory objects and collections is also awesome and makes code more concise, descriptive, readable etc.

I don't think it is quite so mission critical to be able to use linq to specify objects to retrieve from a database though.

Tom Pester
05/29/2009 02:55 PM by
Tom Pester

Hello Dan,

"The blog post you pointed to is entitled "Traversing the ASP.NET Control Tree with LINQ". What does this have to do with a linq provider that is used to retrieve objects that are mapped to tables in a SQL database?"

One of the advantages of linq is it versatility. It pays of to realy understand linq (its operators, capturing, deferred exection,...) because you can leverage it in many domains.

I already know javascript,css,sql,c#,vb.net,xml,.... and for me the true power of linq is that it unifies the way we tink about data, be it relational, in memory, in the cload or just a collection of stuff (asp.net controls).

  • 1 language to rule them all *

I am aware that this is a very bold statement but I am not dissapointend in linq after using it for months. The opposite is true, I keep on discovering new applications where I though it was irrelevant!

So for me to lean yet another language/dsl like hql or the criteria api (correct?) can be avoided if I switch to the linq provider. At the company we work for we use gename which has oql, we use crm which uses "fetch xml" and sharepoint uses CAML. Great fun.

How on earth am I going to be productive in all of these technologies if I have to learn a specific language that I have to use once in a while?

With linq I can just think in terms of sets that I can slice and dice any way I want. A nHibernate noob could write complex queries from day one and if the linq implementation does not "leak" the abstraction.

Tom Pester
05/29/2009 03:08 PM by
Tom Pester

Also, you can combine different linq queries that get their data out of different sources. For example, I can retreive data from sharepoint, materilize it, do the same for nhibernate and combine the sets with linq 2 objects which is the glue between all linq providers.

Stefan Wenig
05/29/2009 03:19 PM by
Stefan Wenig

I'll try to shed some light on the current status of the new LINQ provider.

Steve Strong clearly has the lead here, and I hope I don't misrepresent his part here (especially since he's OOF for a few days, so check back if you expect a comment from him).

What has already happend?

1) Steve has created a new HQL parser that will replace the current one in NH. Steve's parser creates an HQL AST from HQL strings, similar to what Java Hibernate does. The old implementation created SQL straight from HQL text, which looks a bit like a hack in hindsight. I believe that this was not exactly a prerequisite for LINQ support, but it was due and it's clearly a good idea to get this done before starting a project like LINQ 2 NH.

2) Here at rubicon, we made an OSS library for creating LINQ providers, called re-linq: www.re-motion.org/.../...vider-infrastructure.aspx

3) We're in contact with Steve about creating LINQ to NH using re-linq.

What's missing:

1) AFAIK, Steve has not really started to work on a serious LINQ provider, but rather played around with some code to get his feet wet and understand the challenges of such an undertaking.

2) Steve is aware of the effort of doing it all himself, and I see no signs of Not Invented Here with him. He has not yet committed to using re-linq, but to me that's just the natural next step. We've been sitting over this with Ayende briefly 3 weeks ago, and we all agreed that starting from re-linq, it is pretty easy create HQL ASTs.

3) re-linq has always had an ambition to be a generic piece of code, but it was previously only used in our own ORM (re-store, but I'm not going there now). There are some modifications we need to do to re-linq to separate the LINQ parsing part from SQL generation, since we obviously don't need the SQL emitting part for NH. Fabian and Matthias are currently working on these tasks here, they expect to be done in 2 to 3 weeks.

4) There is a large number of possible transformations we can do to the re-linq AST to facilitate query generation, and many of them are of general interest (i.e., not only to NH). So we expect to see some contributions to re-linq as providers begin to tackle more complex cases.

If you care about the details of necessary transformations, you might want to read the discussion that started on Steve's blog and went on in our team blog and Fabian's blog:

blogs.imeta.co.uk/.../688.aspx
www.re-motion.org/.../...vider-infrastructure.aspx
www.re-motion.org/.../...quotfromquot-clauses.aspx

re-linq is not a small effort, and we are quite confident that when Steve returns from his vacation, he will actually be able to create something very useful in quite less than the few months that Frans thinks are unthinkable. It won't be complete from the start, but it should be complete enough and easy to extend.

Stefan

Tom Pester
05/29/2009 04:06 PM by
Tom Pester

Thank you very much for the update Stefan

Chris
05/29/2009 04:26 PM by
Chris

"t is not just because we think everything should be virtual (it should, but that is not a discussion for now)"

mmmmm not sure about that....Im going to have to agree with Anders Hejlsberg on this point
http://www.artima.com/intv/nonvirtual.html

Stefan Wenig
05/29/2009 04:48 PM by
Stefan Wenig

@Chris

that's an interesting point, and I tried to discuss this with Anders at last year's lang.net.

My personal view is that a static override should only be possible if the developer allows for that, for exactly the reasons that Anders usually gives (which is that this changes the interface of the class and needs to be supported later - I didn't read your link but that's what he always says)

But for dynamically generated overloads (code gen) this should not be true. This follows the same reasoning as reflection: statically referencing a private member is evil, but if you only access private members that you discover dynamically, the orginal class remains free to change. Generic serializers are a good example. The same should be true for subclass proxies, which would support any interception framework for AOP or (my pet topic) mixins.

Anders' response was basically, "so if you take a step back, what you really want is a way to dynamically modify any behavior?"

Fair enough, only that we're not going to get anything to enable this, even in .NET 4.0. There's some talk about vNext, but we'll have to wait and see. Maybe the EntLib and EF teams should have pushed the CLR team for an interception infrastructure instead of partial methods...

Chris
05/29/2009 05:06 PM by
Chris

@Stefan

You're right its an excellent discussion and I can see the reasons for and against. I agree with you - it comes down to control and documentation. If everything was virtual how would I know what methods Im supposed to be override (plus there can be no way I could guarantee that you wont chnage core functionality by overriding sensitive methods IYKWIM).

Also this discussion always winds up the Java boys....and thats gotta be good no :)

Steve Strong
05/29/2009 06:39 PM by
Steve Strong

As Stefan said, I'm currently on hols, and "bandwidth limited" - but I'd say that Stefan's summary is pretty close to the mark. Thanks, Stefan, saved me a load of typing on my iPhone :)

Dmitry
05/29/2009 06:42 PM by
Dmitry

@Chris,

You would be able to use "sealed" keyword to prevent overriding methods. The intellisense should be able to to recognize non-sealed methods without any issues. It is just an opt-in/opt-on issue.

Frans Bouma
05/29/2009 06:44 PM by
Frans Bouma

@Stefan: heh :) Of course, with re-linq it might be less than a few months. I was talking about 'here's the tree, this is what you should be emitting, fill the gap' kind of start, which takes alot longer. If re-linq can get this project going it would be great. That might sound strange coming from me, but we too benefit from this, with v3 which will support nhibernate as well (and has a feature to design queries and emit them as linq queries, so it would be great to have linq support from the get go ;))

Stefan Wenig
05/29/2009 09:25 PM by
Stefan Wenig

@Dmitry: no, it's about the difference between a programmer overriding a method and therefore creating an obstacle to change for the original library, and a tool overriding it in some generic manner and adapting to any changes automatically.

@Frans: I've been wondering what your 100-part LINQ DIY blog series was about - helping out the competition or just scaring them away ;-)

@Ayende: Why do they call it defer loading? While MS never really had a consistent ORM story for more than a few months, the one thing they were consistent about is that they think that lazy loading is evil. Maybe avoiding the term made it easier for them?

Ayende Rahien
05/29/2009 10:02 PM by
Ayende Rahien

Peter,

Why try to find a complex example? This is a very simple one that shows the issue.

David Nelson
05/30/2009 02:52 AM by
David Nelson

Frankly, none of this post makes any sense. You are making a lot of unfounded claims about the way EF is going to have to handle certain scenarios, with no evidence at all of why that would have to be the case.

For example, given that EF is proxying the List property, there is no reason why accessing the Count property will have to load the entire collection; it could instead simply execute the required SQL (which, I believe, is exactly what NHibernate does). Nor do I see the "all too many cases" that will require eager loading, simply because the property is virtual.

Your category class is ridiculous; of course if you design a class that accesses a field without initializing it, a NRE will be thrown! What else would you expect? There is nothing about the design of EF that requires you to write your classes in such an inane way.

Also, as has already been mentioned, using the factory methods is not required.

It seems like, once again, you are tilting at windmills from Redmond.

Dmitry
05/30/2009 03:14 AM by
Dmitry

I don't see the problem with NullReferenceException thrown if you do not initialize the collection. It would do the same thing without any proxies unless you expose the setter and initialize it yourself which is a bad practice in my opinion.

Lazy collection loading can be done by querying the context/repository and returning IQueryable <t. Otherwise, it would be a recipe for SELECT N+1 issues.

Fabio Maulo
05/30/2009 05:29 AM by
Fabio Maulo

@Stefan

where is the download of re-linq ?

Stefan Wenig
05/30/2009 06:25 AM by
Stefan Wenig

@Fabio

there's a download link at http://www.re-motion.org/

it's quite large (re-motion is almost the size of NH), but don't let that turn you off. Remotion.Data.Linq.dll is what you need. We can remove the few dependencies from re-linq and provide a neat little assembly you can ilmerge into NH.

We'll post some instructions for using the query model in a few weeks, when the current refactorings are complete and we have something stable enough to write about.

If you really want to waste some time, here's an initial proof-of-concept prototype we made a few weeks ago:

svn.re-motion.org/.../NHibernate.ReLinq/
However, this code abuses our SQL emitter to create HQL strings, which is obviously not an option when we want to generate an HQL AST. The new provider will build directly on the QueryModel, but for that you're going to have to wait for us to finish the tasks I outlined above.

Peter Morris
05/30/2009 10:08 AM by
Peter Morris

@Ayende

I didn't try to find a more complex example, I tried to find a more realistic one, it just happens to be more complicated.

Personally I have never done something like

_products.Add(p);

I always use the property for access. I added my example because

1: I think a number of people might think "That's silly, I wouldn't do that!"

2: There was a more subtle case (which I outlined) which can bite you without you thinking about it. It certainly wasn't obvious to me from the start why we couldn't only make the state properties virtual.

Regards

Pete

Ayende Rahien
05/30/2009 02:31 PM by
Ayende Rahien

David,

I like your use of unfounded. Please check my history with ORM and tell me how unfounded I am.

EF cannot handle the Count. Now if the property type of List <t. List <t doesn't have any extensibility.

If they use IList <t, they would be able to do that, but not with List <t.

As for the Category class, let me show you how it would work with NHibernate:

var category = session.GetCategory;

category.AddProduct(product);// no NRE, NH make sure that AddProduct is virtual and will init the field

With the way the EF has set things up, it is going to cause NRE.

Ayende Rahien
05/30/2009 02:32 PM by
Ayende Rahien

Dmitry,

See my previous example on how you won't get an error with NH.

And lazy loading and queries are two totally different subjects.

Ayende Rahien
05/30/2009 02:35 PM by
Ayende Rahien

Peter,

It is actually quite common for me to expose collections as IEnumerable, and only provide an add method on the owner class

Peter Morris
05/30/2009 04:55 PM by
Peter Morris
I didn't find your example of using the private List
<product field instead of the public List
<product property realistic because it makes no sense that you would do that.
  
  
If your property were IEnumerable then yes, it would make sense, but that's exactly what I did in my example.
>
Dmitry
05/30/2009 08:34 PM by
Dmitry

@Ayende,

Why is it a problem to get an exception when the list is not initialized? That's what happens in .NET. And why would you want IList to act as IQueryable? That is code magic (in a bad way) to me.

Ayende Rahien
05/31/2009 05:25 AM by
Ayende Rahien

Dmitry,

Because when you load the object from the database, it should be initialized.

And I don't want IList to act as IQueryable. I want the ORM to have reasonable default for good OO behavior with regards to performance.

It really bothers me that EF is closing off those options.

Frans Bouma
05/31/2009 10:30 AM by
Frans Bouma

@Stefan: Looking at the code of re-linq, are you afraid of comments? ;)

@Ayende: The 'Count' property access on:

int numberOfOrders = c.Orders.Count;

which will result in the # of orders... it's debatable if this should be a scalar query without fetching the orders. The thing is: if c.Orders is fetched already fetched, it will do it in-memory without refetching, if it's not fetched, aren't you asking for things to be ran in-memory as if c.Orders was already initialized with data? (I'm not saying you should look at this all the time, I just want to give a different PoV on this).

It's the same as: should c.Orders always contain ALL the orders of the customer in 'c', or can you also load for example its last 10 orders in c.Orders? Some will say: "noo!! you should always load all orders, as that's what it represents!" and they do have a point, while others will say: "I don't need all 1000 orders, for this particular scope I need just the last 10 orders and I load them into that property so I can consume that graph in this particular piece of code", and they also have a point.

I.o.w.: it's not black-white what to do. For example:

bool hasOrdersIn2008 = c.Orders.Any(o=]o.OrderDate.Year==2008);

one could argue: if you can do 'Count' in the db, why not this simple EXISTS query as well? I'm not saying you should, I just can imagine that someone could say: "indeed, that's what I want". Of course there are workarounds for this, but it's about what the developer thinks is happening vs. what does happen.

That aside: lazy loading is something that's misused in a lot of scenario's often by developers who have no clue what they're doing. You can limit that a bit with optimizations like the one you described with Count, however, that's not covering the many cases a developer can still accidentally fetch large buckets of data to read just 1 property. Just because intellisense says they can. ;)

Gunnar Liljas
05/31/2009 07:44 PM by
Gunnar Liljas

Don't know if I think that this is informative, or just yet another example of Ayende the Whiner taking a piss at someone else's efforts. I guess it's both. Constructivity points deducted though. Too bad, since constructive criticism of EF is indeed needed.

Dmitry
05/31/2009 08:32 PM by
Dmitry

I agree with what Frans is saying. There are certainly multiple approaches in how to deal with such scenarios.

In my opinion in the object oriented world a developer would expect to have the whole collection in memory when they see IList/ISet/ICollection return types. Otherwise, there would be an IQueryable/IEnumerable return type for the collection.

In order to perform queries orders efficiently there should be a way to do this:

bool hasOrders = orderRepository.Any(o => o.OrderDate.Year == 2008)

EF (by the default convention) uses repositories that are IQueryable properties in the context instance.

Dinesh Gajjar
06/01/2009 09:40 AM by
Dinesh Gajjar

I went through all the comments.

Look on NH Side who is defending

Frans Bouma - Who sells his own closed Source LLBLGen. Eager to attack anything that comes out of MS because his business is threatened!

Ayende Rahien - Making money on nHibernate coaching and Tools. How many sites or production applications have you really worked?? He desperately wants Microsoft to adopt nHibernate so his business booms.

EF is not a mature product either! So guys do youself a favor and don't believe either Microsoft or nHibernate gurus. Do your own reasearch. This blog is nothing more then a publishing blurb. Don't fall in publicity stunts.

Ayende Rahien
06/01/2009 11:00 AM by
Ayende Rahien

Dinesh,

Please try to be at least somewhat accurate.

I have worked on a few dozens production applications, including some pretty big & interesting ones.

I don't work in vacuum, and frankly, I find your accusations insulting.

Karl
06/01/2009 05:07 PM by
Karl

I'm interested to see how it works. I completely see both points of view. Even I think Ayende's example is poor. However, I do think that

-The NH team has more experience with how these things actually turn out in production, and I'd automatically be drawn to their concerns.

-There are plenty of cases where you do want to access the underlying field directly. The best example, already given, is if your property is exposed as IEnumerable. In fact, change Ayende's example from List <product to IEnumerable <product and I'm at a loss as to how you guys aren't seeing it.

I can't help but think that the people defending Microsoft are the same people that constantly expose List <t from their classes. You aren't doing it right.

Dinesh Gajjar
06/02/2009 03:33 AM by
Dinesh Gajjar

@Ayende : I did not mean to insult anyone. Just that I am amazed by the politics of things here. I apologize if it's personal.

@Karl that's your opinion and it's not universal. It all depends on type of development you do. I do "controls development" and no matter how hard I am trying, I am unable to apply DDD to it.

karl
06/02/2009 12:39 PM by
karl

@Dinesh:

Maybe my idea of "Controls Development" is wrong, but what stake do you possibly have in an O/R framework? And just because YOU aren't able to apply DDD, doesn't mean that the Entity Framework should impose non-DDD patterns.

What Ayende is suggesting would allow me to build it the way I want (using IEnumerable where appropriate), and others to do it the way they want (always using List). Through lack of foresight or understanding, the EF won't let me expose IEnumerable (well, not without a lot of pain) - this is a mistake because it violates sound class design principles - which ironically is likely the main market that EF is targeted at.

Mark
06/19/2009 09:52 AM by
Mark

Whilst I kind of agree with some of the points raised here I can't but help this is unproductive.

Hammett has joined Microsoft to try and help build a better product, his knowledge and effort will be a benefit for MS and its users.

I can't help but think that picking holes in EF, wihich is a new framework (thus a little immature) and at the same time saying "EF doesn't work, nHIbernate does" is destructive.

Having had to use nHibernate I can see it has plenty of downsides too, but have I got a blog whining about it ? no.

If everyone tried to work together we'd see much more progress.

cowgaR
07/17/2009 02:07 AM by
cowgaR

ok been through all the discussion and my head is going Ka-Boom right now at 3:55am :)

@Karl, the EF isn't allowing you to expose IEnumerable on navigational properties but you can have ICollection <t there.

Although ICollection <t interface extends IEnumerable <t (and not the other way around) I don't see any showstopper here as most collections implements ICollection <t either. What benefits would you get with IEnumerable here?

According to ADO.NET team any virtual property gets lazy loaded...

So you're not limited to List <t, but I understand having enforced an interface here would allow them to go for better design.

But they even advice you (later) that it is better to use ICollection <t and have all properties virtual if you want to get benefit of change tracking proxies (not to be mixed with lazy loading proxies...)

and I am on the side of initializing navigational properties, just from the nature of POCO that I am responsible for writing the entity class (w/o any thinking about the DB) so there would be error not to initialize it (either in constructor or setter). Or I am missing something here and didn't get this "returned from the DB" what Ayende wrote...

P.S. context.CreateObject <t is only needed for change tracking proxies, so If I'm not wrong (again;-) and you're using snapshot comparison you can just "new up" any object as usuall.

And thanks Ayende for the insight into the problematic, without him there wouldn't even be this "nice" :) discussion.

Comments have been closed on this topic.