nhibernate 3.2: no-proxy not working as expected

1,010 views
Skip to first unread message

Ian Gibson

unread,
Sep 29, 2011, 12:54:16 PM9/29/11
to nhusers
I tried upgrading today to nhibernate 3.2 from 3.1. The motivation for
this was the 'no-proxy' fix in 3.2 that prevents entities from being
eagerly loaded when the association is marked with 'no-proxy'. This is
fixed but now it seems the proxy is returned as opposed to the
underlying type. I am using fluent nhibernate with assembly
redirection on so it redirects to the newest version of nhibernate.
The xml mappings generated for the classes are also included. Example

public class Foo
{
public virtual int Id { get; set; }
public virtual int BarId { get; set; }
public virtual Bar Bar { get; set; }
}

public class Bar
{
public virtual int Id { get; set; }
public virtual int SomeInt { get; set; }
}

public class FooMap : ClassMap<Foo>
{
public FooMap()
{
Table("dbo.foo");
Id(i => i.Id);
Map(i => i.BarId);

References(i => i.Bar)
.LazyLoad(Laziness.Proxy)
.Column("BarId");
}
}

public class BarMap : ClassMap<Bar>
{
public BarMap()
{
Table("dbo.bar");
Id(s => s.Id);
Map(s => s.SomeInt);
}
}

Mapping xml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2"
name="Test.Foo, Test, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null"
table="dbo.foo">
<id name="Id"
type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">

<column name="Id" />
<generator class="identity" />
</id>
<property name="BarId"
type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">

<column name="BarId" />
</property>
<many-to-one class="Test.Bar, Test, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null"
lazy="no-proxy" name="Bar">
<column name="BarId" />
</many-to-one>
</class>
</hibernate-mapping>


Test:

[Test]
public void NoProxyLoadingWorks()
{
using (ISession session =
Util.SessionFactory.OpenSession())
{
Foo foo = session.Get<Foo>(4013);
int someInt = foo.Bar.SomeInt;

Assert.AreEqual("Bar", foo.Bar.GetType().Name);
}
}

Result:

Expected string length 3 but was 8. Strings differ at index 3.
Expected: "Bar"
But was: "BarProxy"
--------------^

This worked in 3.1 and is an example from the mappings file I use
(with changed names). Any assistance on this would be appreciated. I
have never used nhibernate without fluent nhibernate but will put
together a test now using the xml configuration to rule out fluent
nhibernate as being the cause of the issue. I would imagine if the
generated mappings were incorrect I would have experienced an error
building the session factory. The lack of errors during session
factory initialisation implies to me that there isn't an issue with
the mappings.

Thanks in advance.

Gunnar Liljas

unread,
Sep 29, 2011, 7:44:35 PM9/29/11
to nhu...@googlegroups.com
Make the assertion..

Assert.IsInstanceOf<Bar>(foo.Bar);

instead.

The "no-proxy" setting does not imply that the loaded instance will not be a proxy. It implies that it will not be loaded using a proxy. That's a big difference, especially in polymorphic associations.

That said, the behavior is still a bit strange, since the assertion fails if sub properties are accessed before it.

Hmmm....investigating.

/G

Joe Brockhaus

unread,
Sep 29, 2011, 11:28:59 PM9/29/11
to nhu...@googlegroups.com
If i understand you correctly .. You want the association/reference
NOT lazy, correct?

If so, in your fluent mappings .. Where you have .lazyload(..) make it
.Not.LazyLoad(..)

On 9/29/11, Gunnar Liljas <gunnar...@gmail.com> wrote:
> Make the assertion..
>
> Assert.IsInstanceOf<Bar>(foo.Bar);
>
> instead.
>
> The "no-proxy" setting does not imply that the loaded instance will

> not *be*a proxy. It implies that it will
> *not be loaded using* a proxy. That's a big difference, especially in


> polymorphic associations.
>
> That said, the behavior is still a bit strange, since the assertion fails if
> sub properties are accessed before it.
>
> Hmmm....investigating.
>
> /G
>

> --
> You received this message because you are subscribed to the Google Groups
> "nhusers" group.
> To post to this group, send email to nhu...@googlegroups.com.
> To unsubscribe from this group, send email to
> nhusers+u...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/nhusers?hl=en.
>
>

--
Sent from my mobile device

------
Joe Brockhaus
joe.br...@gmail.com
------------

Joe Brockhaus

unread,
Sep 29, 2011, 11:31:38 PM9/29/11
to nhu...@googlegroups.com
I just saw that it worked in 3.1. Interesting. :-x

I have yet to upgrade from 3.1 using fluent as well .. I'll have to
experiment w/ this some more myself. Thx for headsup. If i find
anything i'll letcha know.

Ian Gibson

unread,
Sep 30, 2011, 10:21:48 AM9/30/11
to nhusers
Thanks for the response guys. I want to lazily load the association.
My use case is as follows: I am trying to integrate nhibernate into an
existing system with a decent sized code based. My colleagues and I
are not keen on making all properties virtual. Therefore I have used
interfaces for all the mappings and have managed to work on an
nhibernate mapping assembly to get things working with nhibernate
changing a minimal amount of our current code. Based on this people
have bought into the idea.

Then I got to the issue of lazy loading associations. In all the
mappings I have defined an specific interface has been specified as
the proxy. Due to this I would have to change any associations in our
code base to be the new proxy interface type I have defined. Others on
the team are less enthusiastic about this. There could be side effects
in serialization and other areas I am not aware of (I am relatively
new to the team). So I tried this in nhibernate 3.1:

public class FooMap : ClassMap<Foo>
{
public FooMap()
{
....

References<Bar>(f => ((IFoo)f).IBar)
.LazyLoad(Laziness.NoProxy)
.Column("BarId");
}
}

and the code in the actual class

IBar IFoo.IBar { get; set; }
public Bar Bar
{
get { return ((IFoo) this).IBar as Bar; }
}

I was only toying with this yesterday in a bid to try and change as
little of the code base as possible. The idea of it is to prevent
changing any associations from the concrete type to the proxy
interface type. In 3.1 this code worked. Basically it uses explicit
interface implementation for the lazy lazy loading association. The
normal property then calls onto the explicit interface implementation
performing a cast to get the concrete type. This would allow us to
change only a very small amount of code which and as mentioned this
worked in 3.1 but the association was always eagerly loaded removing
any flexibility with loading. To be clear; this was just an idea and
if designed from the ground up we would use the proxy classes as the
associations instead of the concrete type (really don't like making
all properties virtual). However I am trying to get people bought into
the idea of integrating nhibernate and am trying to sell it on the
basis that a: it won't cause a lot of undesired side effects, b: we
can integrate it iteratively into the code base whilst initially not
changing too much existing code.

Anyway.... I have played around with the settings between 3.2 (fluent
and nhibernate code mappings) and 3.1 (fluent mappings) and seen the
following (screenshot: https://www.sugarsync.com/pf/D6222235_632_46310297)

To explain the results - The columns show the relation type I was
testing, it turns out that the relation type has an effect on the
whether the object is a proxy or the concrete type. Access to the
object also seems to plays a part. Basically the top set of results is
when the association is accessed prior to accessing a property of the
relation, for example

var bar = foo.Bar;
int i = foo.Bar.SomeInt;

The second set of results is when the association property is accessed
without first assigning the assocation to a variable, for example:

int i = foo.Bar.SomeInt;
var bar = foo.Bar;


I am unsure why there would be a difference, I would have thought that
in the second instance foo.Bar is still be accessed so an instance of
something needs to be returned from that call. I'm unsure why
assigning it to a variable first makes a difference. The difference
that is affecting me can be seen in red in spreadsheet screeny linked
above. In 3.1 specifying no proxy caused the association to be of the
concrete type (although eagerly loaded). In 3.2 it is lazily loaded
but is a proxy type if an association property is accessed before the
association itself. I have repeated this test many times to verify the
results but cannot replicate the findings of the test case in the real
domain model I am working on. What I have named bar in the test always
comes back as a proxy using 3.2 although I still messing around with
the mappings to make sure I am testing under the same conditions.

If generally this is something that shouldn't be relied upon
(expecting the concrete type as opposed to the proxy) and what I was
trying to do is considered bad practise then I will have to convince
people that we have to convert to interfaces or use virtual
properties.

The code for all the tests can be found at:

https://www.sugarsync.com/pf/D6222235_632_46136486 - 3.1 test
https://www.sugarsync.com/pf/D6222235_632_46137868 - 3.2 test

The tests (although not in nunit) include mappings, configuration, sql
lite db etc and should be good to run immediately.

Any input appreciated.

Thanks

Ian

On Sep 30, 4:31 am, Joe Brockhaus <fel0ni...@gmail.com> wrote:
> I just saw that it worked in 3.1. Interesting. :-x
>
> I have yet to upgrade from 3.1 using fluent as well .. I'll have to
> experiment w/ this some more myself. Thx for headsup. If i find
> anything i'll letcha know.
>
> On 9/29/11, Joe Brockhaus <fel0ni...@gmail.com> wrote:
>
>
>
>
>
>
>
>
>
> > If i understand you correctly .. You want the association/reference
> > NOT lazy, correct?
>
> > If so, in your fluent mappings .. Where you have .lazyload(..) make it
> > .Not.LazyLoad(..)
>
> > joe.brockh...@gmail.com
> > ------------
>
> --
> Sent from my mobile device
>
> ------
> Joe Brockhaus
> joe.brockh...@gmail.com
> ------------

Joseph Lam

unread,
Oct 25, 2011, 10:12:18 AM10/25/11
to nhusers
I'm having the same problem that the proxy instance returned cannot be
casted into an interface implemented by the concrete type.

Joseph Lam

unread,
Oct 25, 2011, 10:48:03 AM10/25/11
to nhusers

Ian Gibson

unread,
Nov 30, 2011, 8:41:34 AM11/30/11
to nhusers
I have not found a solution for this. Furthermore I experience
additional issues when using proxies with nhibernate. For example,
given the example classes above, you can get errors when doing
something like this

var foo = session.Get<Foo>(10);
var bars = session.CreateCriteria<Bar>().List<Bar>();

If the first example loads foo and foo.Bar as a proxy and the same
session is then used again in example 2 it can cause an exception. The
criteria query will return all bar classes but will resolve things out
of the session if it can. This means the bar which was proxied for the
first query and put in the session will be resolved. When it comes to
the List<Bar> an exception will occur as you cannot put
INhibernateProxyProxy into the a list of type Bar.

I experienced this issue yesterday, the only two solutions I can think
of are:

1 - use session.CreateCriteria<Bar>().List() so untyped values are
returned. An extension method can be made to allow each element to be
unproxied (I can't remember the NHibernate API method for this) and
turned into a generic list of the given type.

2 - Don't use proxies. For me it is very unattractive to have to mark
all properties AND methods virtual. If a class hierarchy exists then
it very hard to understand what can and can't be overridden. It also
means any class can be inherited from and fudged by someone else. This
is not a big problem if you are working on a small application on your
own or with one other person but if you are in a large team building
relatively large software then to me this is a big deal. But it seems
that the proxy support has not been entirely thought out. There are
issues I also get with lazy / eager loading and I wonder what other
issues I will encounter.


On Oct 25, 2:48 pm, Joseph Lam <jla...@gmail.com> wrote:
> This seems related:https://nhibernate.jira.com/browse/NH-2845

Reply all
Reply to author
Forward
0 new messages