WPF not subscribing to PropertyChanged events on proxies generated by NH

405 views
Skip to first unread message

Artur Dorochowicz

unread,
Sep 22, 2008, 1:57:44 PM9/22/08
to nhusers
Hello,

I have a simple hierarchy of classes in my model. I retrieve the root
of the hierarchy with ISession.Get<>(), so that that is my actual
class, and then I initialize the rest of the hierarchy with
NHibernateUtil.Initialize() (so they are just proxies).

Classes in the model implement INotifyPropertyChanged and I use them
in WPF data binding. Change notifications work fine for the root of
the hierarchy (which is my actual class), but does not work with
proxies.

After some debugging I noticed that WPF simply does not subscribe to
PropertyChanged notifications on proxy objects. Even though proxies
implement INotifyPropertyChanged interface and change notifications
are raised.

I use ObjectDataProviders in WPF but same thing happens when I
directly assign object to DataContext property.

I have no clue why that happens. I probably did some silly mistake
somewhere.
Maybe someone have seen something like this.
I will really appreciate any advice on this.

Thanks
Artur

Ayende Rahien

unread,
Sep 22, 2008, 2:02:40 PM9/22/08
to nhu...@googlegroups.com
What happen if you use this ability?

The issue is that the proxy will forward the call to the real object, which is hidden. 
It _should_ forward registration for events as well, though.

Artur Dorochowicz

unread,
Sep 22, 2008, 2:42:49 PM9/22/08
to nhusers
Hi Ayende,

I have read your article earlier. Very interesting, but in order to
drop manual support for INotifyPropertyChanged, I would have to always
instantiate all my model classes through some proxy generator.
Besides, I'm pretty sure change notifications work, it's just that WPF
does not subscribe to them for unknown reason.

I think I will still try your suggestion so that I can better observe
who is subscribing to PropertyChanged notification on proxies (or that
no one actually is).

On 22 Wrz, 20:02, "Ayende Rahien" <aye...@ayende.com> wrote:
> What happen if you use this ability?Advance: Extending NHibernate
> Proxies<http://www.ayende.com/Blog/archive/2007/04/17/Advance-Extending-NHibe...>
>
> The issue is that the proxy will forward the call to the real object, which
> is hidden.
> It _should_ forward registration for events as well, though.
>
> On Mon, Sep 22, 2008 at 8:57 PM, Artur Dorochowicz <
>

Victor Kornov

unread,
Sep 22, 2008, 2:46:12 PM9/22/08
to nhu...@googlegroups.com
Debugging throug FW source is always an option.

Paulo Quicoli

unread,
Sep 22, 2008, 2:32:36 PM9/22/08
to nhu...@googlegroups.com
Hi Arthur,

i'm using NHibernate + WPF with no problems.. could you send me a simple  demo where that occurs ?

thanks

2008/9/22 Artur Dorochowicz <ArturDor...@gmail.com>



--
Paulo R. Quicoli

Membro da comissão editorial da revista ClubeDelphi

Artur Dorochowicz

unread,
Sep 22, 2008, 4:34:03 PM9/22/08
to nhusers
Hi Paulo

I've sent you an e-mail (it's from different e-mail address than I use
here).

WaYdotNET la potenza del .NET nella mani di Carlo

unread,
Sep 22, 2008, 6:26:28 PM9/22/08
to nhu...@googlegroups.com
Hi Artur, if u publish yr sample 4 all ?
Many thx

Carlo Bertini

2008/9/22 Artur Dorochowicz <ArturDor...@gmail.com>



--
WaY dot NET ... pura potenza .NET !!!!

Artur Dorochowicz

unread,
Sep 23, 2008, 12:41:19 PM9/23/08
to nhusers
I'm uploading a VS2008 solution that demonstrates the problem to:
http://sites.google.com/a/dorochowicz.com/artur-public-files/files-1/WPF-NHibernate.zip
The relevant part is in MainWindow.xaml.cs

In the meantime, Paulo Quicoli has provided me with a working
workaround that simply avoids using proxies.

I used something like this:
var rootEntity = session.Get<RootEntityType>( someId );
NHibernateUtil.Initialize( rootEntity.otherEntity );

Paulo showed me how to do this with criteria API, like so:
var rootEntity = session.CreateCriteria(typeof(RootEntityType))
.SetFetchMode("otherEntity", FetchMode.Eager)
.Add(NHibernate.Criterion.Expression.Eq("Id", id))
.UniqueResult<RootEntityType>();


However, as Ayende mentioned, property changed notifications should
work for proxies as well so I'm still interested in your opinions on
this.

Best regards
Artur



On 23 Wrz, 00:26, "WaYdotNET la potenza del .NET nella mani di Carlo"
<waydot...@gmail.com> wrote:
> Hi Artur, if u publish yr sample 4 all ?
> Many thx
>
> Carlo Bertini
>
> 2008/9/22 Artur Dorochowicz <ArturDorochow...@gmail.com>

Ayende Rahien

unread,
Sep 25, 2008, 5:04:01 AM9/25/08
to nhu...@googlegroups.com
I can most certainly see this being registered on the actual class:

+        Method    {Void OnPropertyChanged(System.Object, System.ComponentModel.PropertyChangedEventArgs)}    System.Reflection.MethodInfo {System.Reflection.RuntimeMethodInfo}
+        Target    {System.ComponentModel.PropertyChangedEventManager}    object {System.ComponentModel.PropertyChangedEventManager}

And the invocation list contains both of them.

The problem, however, is that the sender object that it sends is different than the proxy.
WPF thinks that it is the proxy that it is handling, while the real object behind the scene is raising the event, WPF catch that, but has nothing that match this (because the proxy and the real object are different instances).

This is a leaking this issue, I am afraid.

Artur Dorochowicz

unread,
Sep 25, 2008, 9:43:43 AM9/25/08
to nhusers
Now that you've described it, it makes perfect sense. Thanks Ayende.

I use Paulo's solution now, so that it is no issue for me anymore,
but, just out of curiosity, are there any possible solutions for this?

One (cumbersome) that I can see is to use some kind of interceptor as
you do in your article, in the model make SendPropertyChanged
protected virtual and intercept calls to it. The interceptor, instead
of calling the original SendPropertyChanged, would raise the
PropertyChanged event but with sender being the proxy object.
Could that work?
I don't know neither reflection nor castle proxy stuff well enough to
go and try to do it myself.

Ayende Rahien

unread,
Sep 25, 2008, 11:37:09 AM9/25/08
to nhu...@googlegroups.com
Yes, your solution would be the probably best solution for that.

Rob

unread,
Oct 7, 2008, 3:06:05 PM10/7/08
to nhusers
I have just spent a significant amount of time debugging
PropertyChanged events that weren't being triggered and was happy to
find this post which describes my problem exactly. The workaround of
not lazy-loading objects is not an option for us so we're stuck with
not having the ability to properly use INotifyPropertyChanged in our
code. The solution of having an interceptor is beyond what we trust
ourselves to do in NHibernate. Has anyone attempted this?

On Sep 25, 10:37 am, "Ayende Rahien" <aye...@ayende.com> wrote:
> Yes, your solution would be the probably best solution for that.
>
> On Thu, Sep 25, 2008 at 4:43 PM, Artur Dorochowicz <
>

Artur Dorochowicz

unread,
Oct 9, 2008, 6:59:47 AM10/9/08
to nhusers
I made an attempt, but it doesn't work (and seems ugly) (this is
different than what I describe in earlier post, because I don't know
how to do that):

Define an interface that your domain classes will implement:

public interface IProxyableNotifyPropertyChanged :
INotifyPropertyChanged
{
object Sender { get; }
}

In your class use it like this:

public class MyClass : IProxyableNotifyPropertyChanged
{
....

public virtual event PropertyChangedEventHandler PropertyChanged;

public virtual object Sender
{
get { return this; }
}

private void SendPropertyChanged( string propertyName )
{
if( PropertyChanged != null )
{
PropertyChanged( Sender, new
PropertyChangedEventArgs( propertyName ) );
}
}
}


The proxy factory-related code (it's for NH 2.0.x, adapted from
Ayende's code from:
https://svn.sourceforge.net/svnroot/nhibernate/trunk/nhibernate/src/NHibernate.Test/ProxyInterface/CustomProxyFixture.cs
)

public class CustomProxyFactoryFactory : IProxyFactoryFactory
{
public IProxyFactory BuildProxyFactory()
{
return new DataBindingProxyFactory();
}
}

public class DataBindingProxyFactory : CastleProxyFactory
{
public override INHibernateProxy GetProxy( object id,
ISessionImplementor session )
{
try
{
CastleLazyInitializer initializer =
new DataBindingInterceptor( EntityName, PersistentClass, id,
GetIdentifierMethod, SetIdentifierMethod,
ComponentIdType, session );

object generatedProxy = null;
if ( IsClassProxy )
{
generatedProxy = DefaultProxyGenerator.CreateClassProxy(
PersistentClass, Interfaces, ProxyGenerationOptions.Default,
initializer );
}
else
{
generatedProxy =
DefaultProxyGenerator.CreateInterfaceProxyWithoutTarget(
Interfaces[0], Interfaces, initializer );
}

initializer._constructed = true;
return (INHibernateProxy) generatedProxy;
}
catch( Exception e )
{
throw new HibernateException( "Creating a proxy instance failed",
e );
}
}
}


public class DataBindingInterceptor : CastleLazyInitializer
{
public DataBindingInterceptor(string entityName, System.Type
persistentClass, object id,
MethodInfo getIdentifierMethod, MethodInfo setIdentifierMethod,
IAbstractComponentType componentIdType, ISessionImplementor session)
: base(entityName, persistentClass, id, getIdentifierMethod,
setIdentifierMethod, componentIdType, session)
{
}

public override void Intercept(IInvocation invocation)
{
if (invocation.Method.DeclaringType ==
typeof(IProxyableNotifyPropertyChanged)
&& invocation.Method.Name == "get_Sender" )
{
invocation.ReturnValue = invocation.InvocationTarget;
return;
}
base.Intercept(invocation);
}
}

You tell NHibernate to use your proxy factory like this:

configuration.Properties[NHibernate.Cfg.Environment.ProxyFactoryFactoryClass]
=
typeof(CustomProxyFactoryFactory).AssemblyQualifiedName;


It doesn't work, because DataBindingInterceptor.Intercept() is never
called with "get_Sender" method, don't know why, looks like the
generated proxy does not provide an override for that property, so the
CLR calls the property on the actual object and not on a proxy.

Maybe someone with more experience in this area will be able to help.
Reply all
Reply to author
Forward
0 new messages