--
You received this message because you are subscribed to the Google Groups "Fluent NHibernate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fluent-nhibernate+unsubscribe@googlegroups.com.
To post to this group, send email to fluent-nhibernate@googlegroups.com.
return (TCastTarget)this;You can put something like this in relevant base class:public TCastTarget As<TCastTarget>()
{}
/Oskar
To unsubscribe from this group and stop receiving emails from it, send an email to fluent-nhibern...@googlegroups.com.
To post to this group, send email to fluent-n...@googlegroups.com.
> On Aug 10, 2016, at 10:55 AM, Michael Powell <mwpow...@gmail.com> wrote:
>
> Hello,
>
> I have a use case where I'd like to cast an abstract base class to a
> child class in order to verify one of the child class properties.
>
> Consider, an albeit somewhat contrived example:
>
> abstract class Juice { }
> class AppleJuice : Juice { public virtual double Acidity { get; set; } }
> class OrangeJuice : Juice { public virtual double Tanginess { get; set; } }
>
> class Cup { public virtual Juice Juice { get; set; } }
>
> I would like to test the Juice in the Cup in this manner:
>
> Cup cup = ...;
> if (((ApplyJuice) cup.Juice).Acidity > 1.5)
> {
> // Respond to acidity...
> }
>
> However, what I am finding is that the cast doesn't work "properly",
> due to the proxies involved, I get InvalidCastException:
>
> "Unable to cast object of type 'JuiceProxy' to type 'AppleJuice'"
>
> Which I suppose is not surprising, but I wondered with the support for
> class hierarchies, is there a recommended workaround? Do I need to do
> some other form(s) of casting, or have some event handlers connecting
> with ISession bits?
I solved this by using a core non-persistent class that implements the properties of Cup, then a persistent class that inherits from Cup that implements my Hibernate and persistent layer (created by, created on, etc).
Then I can cast the object to the non-persistent class when I need to ensure the proxies aren’t getting in the way. Unfortunately that was an early design decision and isn’t easy to implement on a larger/older DAL.
Just my 2c.
To unsubscribe from this group and stop receiving emails from it, send an email to fluent-nhibernate+unsubscribe@googlegroups.com.
To post to this group, send email to fluent-nhibernate@googlegroups.com.
That's exactly Oskar's trick: You call the proxy, into the proxied object and get it to cast itself.
To unsubscribe from this group and stop receiving emails from it, send an email to fluent-nhibernate+unsubscribe@googlegroups.com.
To post to this group, send email to fluent-nhibernate@googlegroups.com.
You can't cast the proxy. You can cast the proxied object. Therein lies the difference.
I try to avoid casting anyways. I'd rather use a visitor pattern as it maintains completeness.
On Wednesday, August 10, 2016 at 1:20:15 PM UTC-4, Oskar Berggren wrote:return (TCastTarget)this;You can put something like this in relevant base class:public TCastTarget As<TCastTarget>()
{}Do you understand what's going on with Proxying? You don't see it in your code, but what you end up with is something like this:class JuiceProxy : Juice { ... }class AppleJuiceProxy : AppleJuice { ... }class OrangeJuiceProxy : OrangeJuice { ... }Something like that, if memory serves, but I haven't verified the actual inheritance tree.And since Cup wants a Juice, JuiceProxy fits the bill, but there is no reasonable way for JuiceProxy to be side-cast into the expected AppleJuice, much less AppleJuiceProxy, without losing necessary hierarchical details.
To unsubscribe from this group and stop receiving emails from it, send an email to fluent-nhibernate+unsubscribe@googlegroups.com.
To post to this group, send email to fluent-nhibernate@googlegroups.com.
2016-08-11 11:47 GMT+01:00 mwpowellhtx <mwpow...@gmail.com>:
On Wednesday, August 10, 2016 at 1:20:15 PM UTC-4, Oskar Berggren wrote:return (TCastTarget)this;You can put something like this in relevant base class:public TCastTarget As<TCastTarget>()
{}Do you understand what's going on with Proxying? You don't see it in your code, but what you end up with is something like this:class JuiceProxy : Juice { ... }class AppleJuiceProxy : AppleJuice { ... }class OrangeJuiceProxy : OrangeJuice { ... }Something like that, if memory serves, but I haven't verified the actual inheritance tree.And since Cup wants a Juice, JuiceProxy fits the bill, but there is no reasonable way for JuiceProxy to be side-cast into the expected AppleJuice, much less AppleJuiceProxy, without losing necessary hierarchical details.
2016-08-11 11:47 GMT+01:00 mwpowellhtx <mwpow...@gmail.com>:
On Wednesday, August 10, 2016 at 1:20:15 PM UTC-4, Oskar Berggren wrote:return (TCastTarget)this;You can put something like this in relevant base class:public TCastTarget As<TCastTarget>()
{}Do you understand what's going on with Proxying? You don't see it in your code, but what you end up with is something like this:class JuiceProxy : Juice { ... }class AppleJuiceProxy : AppleJuice { ... }class OrangeJuiceProxy : OrangeJuice { ... }Something like that, if memory serves, but I haven't verified the actual inheritance tree.And since Cup wants a Juice, JuiceProxy fits the bill, but there is no reasonable way for JuiceProxy to be side-cast into the expected AppleJuice, much less AppleJuiceProxy, without losing necessary hierarchical details.If you haveAppleJuice : Juiceyou will getJuiceProxy : Juicebut the important thing is that if you call a method on Juice:myJuice.As<AppleJuice>()then if myJuice is a JuiceProxy, it will override the As<>() method, trigger any required initialization, AND THEN FORWARD THE CALL TO THE REAL OBJECT. And the real object will be an instance of AppleJuice. So when the body of the As() method runs, the this-pointer will be the real, derived, object, that you can cast.
On Thursday, August 11, 2016 at 7:59:46 AM UTC-4, Oskar Berggren wrote:2016-08-11 11:47 GMT+01:00 mwpowellhtx <mwpow...@gmail.com>:
On Wednesday, August 10, 2016 at 1:20:15 PM UTC-4, Oskar Berggren wrote:return (TCastTarget)this;You can put something like this in relevant base class:public TCastTarget As<TCastTarget>()
{}Do you understand what's going on with Proxying? You don't see it in your code, but what you end up with is something like this:class JuiceProxy : Juice { ... }class AppleJuiceProxy : AppleJuice { ... }class OrangeJuiceProxy : OrangeJuice { ... }Something like that, if memory serves, but I haven't verified the actual inheritance tree.And since Cup wants a Juice, JuiceProxy fits the bill, but there is no reasonable way for JuiceProxy to be side-cast into the expected AppleJuice, much less AppleJuiceProxy, without losing necessary hierarchical details.Laziness.NoProxy solves the problem. TBD what I'm giving up in proxies, but so far so good.
/Oskar
On Thursday, August 11, 2016 at 7:23:35 AM UTC-4, Rasmoo wrote:That's exactly Oskar's trick: You call the proxy, into the proxied object and get it to cast itself.No, that's not what Oskar wrote, one the one hand; see below:public TCastTarget As<TCastTarget>()return (TCastTarget)this;
{}This is not any different than what I attempted in the LINQ statement.
On the other hand, introducing an "UnproxiedObject" virtual property is too much. I want to keep the model itself as clean as possible without inverting proxy dependency inversions more than is necessary.
2016-08-11 12:33 GMT+01:00 mwpowellhtx <mwpow...@gmail.com>:
On Thursday, August 11, 2016 at 7:23:35 AM UTC-4, Rasmoo wrote:That's exactly Oskar's trick: You call the proxy, into the proxied object and get it to cast itself.No, that's not what Oskar wrote, one the one hand; see below:public TCastTarget As<TCastTarget>()return (TCastTarget)this;
{}This is not any different than what I attempted in the LINQ statement.It is in fact completely different. It is applying the cast to an entirely different object.
On the other hand, introducing an "UnproxiedObject" virtual property is too much. I want to keep the model itself as clean as possible without inverting proxy dependency inversions more than is necessary.It is possible to provide both the As<T>() and "Unproxy()" methods as extension methods instead of putting them in each class. The difference is that the body of the method then need to explicitly check if the object is a proxy and ask NHibernate for the real object before applying the cast.
Untested, but something like this:public static T As<T>(this object obj)
{if (obj is INHibernateProxy)obj = ((INHibernateProxy)obj).HibernateLazyInitializer.GetImplementation();return (T)obj;}
So as you say, you can't cast an instance of JuiceProxy to a derived type, but you can extract the real object from the proxy and then cast that. This is what the various "tricks" I've shown you do.
On Thursday, August 11, 2016 at 8:22:07 AM UTC-4, Oskar Berggren wrote:2016-08-11 12:33 GMT+01:00 mwpowellhtx <mwpow...@gmail.com>:
On Thursday, August 11, 2016 at 7:23:35 AM UTC-4, Rasmoo wrote:That's exactly Oskar's trick: You call the proxy, into the proxied object and get it to cast itself.No, that's not what Oskar wrote, one the one hand; see below:public TCastTarget As<TCastTarget>()return (TCastTarget)this;
{}This is not any different than what I attempted in the LINQ statement.It is in fact completely different. It is applying the cast to an entirely different object.How so? Watch what I did there.cup.Juice is a type of Juice, correct?
So I want ((AppleJuice) cup.Juice). With me so far?
Adding Juice.As<TJuice>() { return (TJuice) this; } is no different than doing the cast external to the instance.
On the other hand, introducing an "UnproxiedObject" virtual property is too much. I want to keep the model itself as clean as possible without inverting proxy dependency inversions more than is necessary.It is possible to provide both the As<T>() and "Unproxy()" methods as extension methods instead of putting them in each class. The difference is that the body of the method then need to explicitly check if the object is a proxy and ask NHibernate for the real object before applying the cast.Well, I wouldn't put them in "each" class, for this contrived example, but usually that sort of thing would end up in a base class. Still, not very "Liskov", injecting DAL concerns in the domain layer.
2016-08-11 13:33 GMT+01:00 mwpowellhtx <mwpow...@gmail.com>:
On Thursday, August 11, 2016 at 8:22:07 AM UTC-4, Oskar Berggren wrote:2016-08-11 12:33 GMT+01:00 mwpowellhtx <mwpow...@gmail.com>:
On Thursday, August 11, 2016 at 7:23:35 AM UTC-4, Rasmoo wrote:That's exactly Oskar's trick: You call the proxy, into the proxied object and get it to cast itself.No, that's not what Oskar wrote, one the one hand; see below:public TCastTarget As<TCastTarget>()return (TCastTarget)this;
{}This is not any different than what I attempted in the LINQ statement.It is in fact completely different. It is applying the cast to an entirely different object.How so? Watch what I did there.cup.Juice is a type of Juice, correct?In this line you are attempting to cast the juiceProxy, which as expected won't work:So I want ((AppleJuice) cup.Juice). With me so far?But here, you need to take a moment to ponder which object the "this" pointer really points to:Adding Juice.As<TJuice>() { return (TJuice) this; } is no different than doing the cast external to the instance.Hint: It is NOT the proxy instance.In memory you will haveproxyInstance [points to]-> realInstance (realInstance present _after_ it's been required for the first time)And the call path will be something like this:proxyInstance.As<ConcreteType>() [calls]-> realInstance.As<ConcreteType>() { so when code here runs, "this" is realInstance, not proxyInstance }So as I said, the cast is being applied to a completely different object, compared to when you apply the cast "externally". And that is a very important difference, because it will actually work.
On the other hand, introducing an "UnproxiedObject" virtual property is too much. I want to keep the model itself as clean as possible without inverting proxy dependency inversions more than is necessary.It is possible to provide both the As<T>() and "Unproxy()" methods as extension methods instead of putting them in each class. The difference is that the body of the method then need to explicitly check if the object is a proxy and ask NHibernate for the real object before applying the cast.Well, I wouldn't put them in "each" class, for this contrived example, but usually that sort of thing would end up in a base class. Still, not very "Liskov", injecting DAL concerns in the domain layer.The benefit of having it in the base class is that the implementation is self-contained in the model, and doesn't depend on NHibernate. It will work regardless.But yes, the fact that you need to use a special method instead of the normal cast syntax is a leak in the ORM abstraction. We must be pragmatic and apply the As<>() workaround, or try to use a pattern that avoids the need for the cast.
/Oskar