>From what I can tell, this is so far doing the right thing from an Equals
perspective. We prefer to see the primitive P type by default, but
apparently can also see the returned R type, so normalize accordingly.
protected virtual bool Equals(P x, P y) => x is IEquatable<P> xe && y is
IEquatable<P> ye && xe.Equals(ye);
public virtual new bool Equals(object x, object y)
{
object Normalize(object value) => value switch
{
DBNull or null => DBNull.Value,
P p => p,
R r => Disassemble(r),
_ => throw new InvalidOperationException($"Unexpected value type:
'{value.GetType().FullName}'.")
};
var (xnorm, ynorm) = (Normalize(x), Normalize(y));
return (xnorm is null && ynorm is null)
|| (xnorm is DBNull && ynorm is DBNull)
|| ReferenceEquals(x, y)
|| (xnorm is P xp && ynorm is P yp && Equals(xp, yp));
}
Another concern is getting the hash code; ostensibly we think this may
suffer from the same P versus R behavior patterns as Equals exhibits.
Initially taking the naive approach that we are expecting the primitive P
type and that's it. But wondering if we might also see the returned R type
there as well.
protected virtual int GetHashCode(P p) => p?.GetHashCode() ?? default;
public virtual int GetHashCode(object x) => x is P p ? GetHashCode(p) :
throw new InvalidOperationException(
$"Value {(x is null ? "null" : $"{x}")} of the incorrect type: '{(x is
null ? "null" : typeof(P).FullName)}'"
);
Taking a step back from equality and hash codes, could that perhaps be an
indication that one of the cache directions may be assembling or
disassembling a value somehow incorrectly, perhaps? Which is entirely
possible; although overall we went to some lengths to ensure that the
diretion was properly, from database assemble, and to database disassemble.
Thoughts? Anyone?
On Friday, July 25, 2025 at 11:46:28 PM UTC-4 Michael W Powell wrote:
> The docs as do the comments claim Equals compares the _persistent state_
> which I take to mean the PRIMITIVE (P) type.
>
> Take for instance, we have a DECIMAL database mapping, to a CLR UINT64 in
> the apporpriate scale and precision.
>
> Equals is not seeing DECIMAL, which I would expect, but rather, UINT64.
> Which is part and partial my confusion over this issue.
>
> In the following base implementation, assuming NH docs, comments, etc, are
> accurate, then this should never throw. But it is throwing, because object
> value is UINT64.
>
> public virtual new bool Equals(object x, object y)
> {
> static object Normalize(object value) => value switch
> {
> DBNull or null => DBNull.Value,
> P p => p,
> _ => throw new InvalidOperationException($"Unexpected value type:
> '{value.GetType().FullName}'.")
> };
>
> var (xp, yp) = (Normalize(x), Normalize(y));
>
> return (xp is null && yp is null)
> || (xp is DBNull && yp is DBNull)
> || ReferenceEquals(x, y)
> || Equals(xp, yp);
> }
>
> So my guess is, we want to be comparing values of type P, but can also see
> values of type R (returned types), so need to disassemble them accordingly,
> presumably from cached.
>
> Anyone with any insight into this, please chime in? Thanks...
>
> On Friday, July 25, 2025 at 11:25:43 PM UTC-4 Michael W Powell wrote:
>
>> Perhaps making it harder than it needs to be. The interfaces both
>> indicating literally equality and hash codes _of the persistent state_ that
>> is of the P primitive types, I gather. If so no biggie, that's a simple
>> correction to make.
>>
>> On Friday, July 25, 2025 at 10:58:19 PM UTC-4 Michael W Powell wrote:
>>
>>> Hello,
>>>
>>> I am at a point now implementing some user types, things are loading
>>> from the database correctly, I can work with the models, make adjustments
>>> via services and WPF UI views, view models, all that is working beautifully.
>>>
>>> Now I am doing the last leg of the game plan: persisting back to the
>>> database, which as I can gather from the traces, StackOverflowExceptions,
>>> etc, revolves around negotiating Equals: A LOT, and a lot of navigation to
>>> and/or from assembed and/or dissassembled form factors. Which depending
>>> upon the user type implementation, can be tricky.
>>>
>>> Which is part partial my question. What is the general expection, i.e.
>>> persistence 'protocol' from an NHibernate perspective, navigating the
>>> persistence conversation.
>>>
>>> Our approach is also generally to implement a P or R based generic user
>>> type at base (i.e. primitive versus returned types), especially when we
>>> want to do things such as negotiate NodaTime constructs, sometimes also
>>> JSON based Newtonsoft.Json.Linq constructs; whch as I mentioned, works
>>> beautifully querying and loading from the database.
>>>
>>> From what I can also determine, user types sometimes also cached,
>>> although from the lack of documentation, we are not hundred percent clear
>>> in which form, either P or R.
>>>
>>> I've implemented the assembly and disassembly generally to use switch
>>> expressions with strategically placed pattern matching in order to isolate
>>> P from R in a broad range of use cases. But still finding a StackOverflow
>>> slip through the cracks here and there.
>>>
>>> Overall, we are familiar with ORM in general, usually involving
>>> comparison between two datasets, so I'd guess minimally at a primitive
>>> level, but what is cached, the assembled version, and to make the
>>> apporpriate comparison that does not blow up the stack.
>>>
>>> So I am here to ask the question: what sort of protocol, assy, disassy,
>>> caching, can we expect, negotiating the persistence sequence?
>>>
>>> Thanks!
>>>
>>> Best,
>>>
>>> Michael W. Powell
>>>
>>>
--
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 [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/fluent-nhibernate/46325334-993d-4105-949a-7c3b8d620cf0n%40googlegroups.com.