Oops,

   Ignore the "fixed" code, it was still half-baked, and there were a
few problems with it. First, I didn't notice that
AbstractEntityPersister.ForceVersionIncrement() also would need this
same check, and that the version update would actually fail (because
WHERE version = 0 isn't the same as WHERE version is null).

   Next I tried another approach (change all the .Next() methods on
the integer types -- see attached patch) but that causes a different
exception. (see below my signature).

   I think the real fix involves the default unsaved-value being both
NULL and 0 for nullable types (or a special case somewhere around
this), which I'm not sure how to accomplish...

Regards,
Mike

failed: NHibernate.StaleObjectStateException : Row was updated or
deleted by another transaction (or unsaved-value mapping was
incorrect): [MyCompany.Example#42]
        Persister\Entity\AbstractEntityPersister.cs(2172,0): at
NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32 rows,
Object id, Int32 tableNumber, IExpectation expectation, IDbCommand
statement)
        Persister\Entity\AbstractEntityPersister.cs(1618,0): at
NHibernate.Persister.Entity.AbstractEntityPersister.ForceVersionIncrement(Object
id, Object currentVersion, ISessionImplementor session)
        Event\Default\AbstractLockUpgradeEventListener.cs(64,0): at
NHibernate.Event.Default.AbstractLockUpgradeEventListener.UpgradeLock(Object
entity, EntityEntry entry, LockMode requestedLockMode,
ISessionImplementor source)
        Event\Default\DefaultLockEventListener.cs(55,0): at
NHibernate.Event.Default.DefaultLockEventListener.OnLock(LockEvent
event)
        Impl\SessionImpl.cs(2470,0): at
NHibernate.Impl.SessionImpl.FireLock(LockEvent lockEvent)
        Impl\SessionImpl.cs(776,0): at
NHibernate.Impl.SessionImpl.Lock(Object obj, LockMode lockMode)

On Tue, Nov 16, 2010 at 4:14 PM, Mike Pontillo <[email protected]> wrote:
> Hi Fabio,
>
>   Thanks for the response. A custom type seemed like overkill here
> since all I really want is a nullable Int32. I ended up doing three
> things to work around this problem:
>
>  - Made the version property in my POCO nullable (int?) to solve the
> problem where NHibernate found a "dirty" (but not really dirty) object
> in the database, since its version property was mistakenly set to null
>  - Made the version property in my mapping XML just an "int" (not sure
> if it necessary to call out that it's nullable in the mapping XML, but
> I got the exception noted below when I did -- it works when I leave
> out the type as well, of course.)
>  - Made the following code change (to fix the NullReferenceException
> when NHibernate tries to increment the null value in the database):
>
> --- Versioning.cs
> +++ Versioning.cs       (working copy)
> @@ -28,6 +28,11 @@
>            /// <returns>Returns the next value for the version.</returns>
>            public static object Increment(object version,
> IVersionType versionType, ISessionImplementor session)
>            {
> +            if(version == null)
> +            {
> +                version = versionType.Seed(session);
> +            }
> +
>            object next = versionType.Next(version, session);
>            if (log.IsDebugEnabled)
>            {
>
>   By the way, I also tested to verify that the 3rd change was really
> necessary. (Since I saw Seed() being used elsewhere, I wasn't sure if
> it would try again to increment a null value after I fixed my
> mappings.)
>
>   Also, in case anyone else is seeing this problem, the following
> exception is thrown when I try to specify the type of the version
> property in the XML as "int?":
>
> NHibernate.MappingException : Could not compile the mapping document:
> Example.hbm.xml
>  ----> NHibernate.MappingException : Could not determine type for:
> MyCompany.Model.int?, PROLIN.DAO, for columns:
> NHibernate.Mapping.Column(VERSION)
> at NHibernate.Cfg.Configuration.LogAndThrow(Exception exception) in
> Configuration.cs: line 340
> at NHibernate.Cfg.Configuration.AddDeserializedMapping(HbmMapping
> mappingDocument, String documentFileName) in Configuration.cs: line
> 528
> at NHibernate.Cfg.Configuration.AddValidatedDocument(NamedXmlDocument
> doc) in Configuration.cs: line 497
> at NHibernate.Cfg.Configuration.ProcessMappingsQueue() in
> Configuration.cs: line 1830
> at NHibernate.Cfg.Configuration.AddDocumentThroughQueue(NamedXmlDocument
> document) in Configuration.cs: line 1821
> at NHibernate.Cfg.Configuration.AddXmlReader(XmlReader hbmReader,
> String name) in Configuration.cs: line 1814
> at NHibernate.Cfg.Configuration.AddInputStream(Stream xmlInputStream,
> String name) in Configuration.cs: line 644
> at NHibernate.Cfg.Configuration.AddResource(String path, Assembly
> assembly) in Configuration.cs: line 682
> at NHibernate.Cfg.Configuration.AddAssembly(Assembly assembly) in
> Configuration.cs: line 761
> --MappingException
> at NHibernate.Mapping.SimpleValue.get_Type() in SimpleValue.cs: line 241
> at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.BindProperty(HbmVersion
> versionSchema, Property property, IDictionary`2 inheritedMetas) in
> RootClassBinder.cs: line 227
> at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.BindVersion(HbmVersion
> versionSchema, PersistentClass rootClass, Table table, IDictionary`2
> inheritedMetas) in RootClassBinder.cs: line 209
> at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.Bind(HbmClass
> classSchema, IDictionary`2 inheritedMetas) in RootClassBinder.cs: line
> 55
> at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddRootClasses(HbmClass
> rootClass, IDictionary`2 inheritedMetas) in MappingRootBinder.cs: line
> 83
> at 
> NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddEntitiesMappings(HbmMapping
> mappingSchema, IDictionary`2 inheritedMetas) in MappingRootBinder.cs:
> line 42
> at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind(HbmMapping
> mappingSchema) in MappingRootBinder.cs: line 29
> at NHibernate.Cfg.Configuration.AddDeserializedMapping(HbmMapping
> mappingDocument, String documentFileName) in Configuration.cs: line
> 520
>
>   This exception may be an error on my part rather than a bug, since
> I am not explicitly calling out nullable properties anywhere else in
> my mapping XML. (I imagine I was trying to do something unsupported,
> but I don't explicitly state types anywhere else in my mapping XML.)
> The quirk here is that for a version property (according to the
> reference manual I found at
> http://www.nhforge.org/doc/nh/en/index.html, section 5.1.7), the
> "type" parameter is "(optional - defaults to Int32)". I'm not sure why
> this wouldn't default to the type defined in the POCO for the version
> property. Also, the manual states "Version numbers may be of type
> Int64, Int32, Int16, Ticks, Timestamp, or TimeSpan (or their nullable
> counterparts in .NET 2.0)", so I assumed I could write "int?".
>
> Regards,
> Mike
>
> On Thu, Nov 11, 2010 at 6:12 AM, Fabio Maulo <[email protected]> wrote:
>> May be you have to know that you can implements your own type for the
>> version property following the rules of your legacy DB.
>>
>> --
>> Fabio Maulo
>>
>>
>> El 10/11/2010, a las 20:04, Mike Pontillo <[email protected]> escribió:
>>
>>> In case anyone was wondering,
>>>
>>>   I figured out the problem. I am trying to map a legacy database
>>> that uses version columns that are nullable. NHibernate threw
>>> exceptions when I defined the nullable types in my POCOs and the
>>> NHibernate XML (even though the manual stated that they were supported
>>> -- so likely a bug) so I defined them as "int". However, I didn't
>>> notice that some rows (very few - likely mistakes, or cases where
>>> someone hand-edited the data) in the database indeed have NULL values
>>> for the version column. If a query ever cached one of these NULL
>>> values, and NHibernate subsequently performed a dirty check, it will
>>> throw this exception.
>>>
>>>   If I have time, I'll write up a test case and try patching the code
>>> so NHibernate supports nullable version columns better. I think if
>>> NHibernate treated NULL version columns as if they had the value 0,
>>> this would fix the problem.
>>>
>>> Regards,
>>> Mike
>>>
>>> On Tue, Nov 9, 2010 at 5:30 PM, Mike Pontillo <[email protected]> wrote:
>>>> Greetings,
>>>>
>>>>   I am using NHibernate 3.0.0 beta 2, and am trying to evolve some
>>>> prototype code that was using the "session per call" anti-pattern to
>>>> use a "session per request" approach. I soon noticed that after
>>>> implementing the session sharing, my unit tests started failing with
>>>> the following exception:
>>>>
>>>> System.NullReferenceException : Object reference not set to an
>>>> instance of an object.
>>>> at NHibernate.Type.Int32Type.Next(Object current, ISessionImplementor
>>>> session) in d:\CSharp\NH\nhibernate\src\NHibernate\Type\Int32Type.cs:
>>>> line 77
>>>> at NHibernate.Engine.Versioning.Increment(Object version, IVersionType
>>>> versionType, ISessionImplementor session) in d:\CSharp\NH\nhibernate
>>>> \src\NHibernate\Engine\Versioning.cs: line 31
>>>> at
>>>> NHibernate.Event.Default.DefaultFlushEntityEventListener.GetNextVersion(FlushEntityEvent
>>>> event) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default
>>>> \DefaultFlushEntityEventListener.cs: line 331
>>>> at
>>>> NHibernate.Event.Default.DefaultFlushEntityEventListener.ScheduleUpdate(FlushEntityEvent
>>>> event) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default
>>>> \DefaultFlushEntityEventListener.cs: line 242
>>>> at
>>>> NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent
>>>> event) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default
>>>> \DefaultFlushEntityEventListener.cs: line 45
>>>> at
>>>> NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent
>>>> event) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default
>>>> \AbstractFlushingEventListener.cs: line 161
>>>> at
>>>> NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent
>>>> event) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default
>>>> \AbstractFlushingEventListener.cs: line 60
>>>> at
>>>> NHibernate.Event.Default.DefaultDirtyCheckEventListener.OnDirtyCheck(DirtyCheckEvent
>>>> event) in d:\CSharp\NH\nhibernate\src\NHibernate\Event\Default
>>>> \DefaultDirtyCheckEventListener.cs: line 21
>>>> at NHibernate.Impl.SessionImpl.IsDirty() in d:\CSharp\NH\nhibernate\src
>>>> \NHibernate\Impl\SessionImpl.cs: line 1510
>>>>
>>>>   The failures happened at "random" times, such as when I was about
>>>> to execute a query and NHibernate would do a dirty check. So I added
>>>> asserts for session.IsDirty() to try to catch the problem earlier, but
>>>> I still don't see an obvious cause. The odd thing is, the version
>>>> property should not be incrementing, since I am only doing read-only
>>>> work within the session so far. For example, the following code fails:
>>>>
>>>>
>>>> Assert.IsFalse(session.IsDirty());
>>>>            var query = session.CreateQuery("from " + typeof(T).Name);
>>>>            var list = query.List<T>();
>>>>            Console.WriteLine(" HQL query: {0} " + typeof(T).Name + "
>>>> objects found", list.Count());
>>>>            Assert.AreEqual(_rowCount, list.Count()); // value cached
>>>> in test setup
>>>>            Assert.IsFalse(session.IsDirty());
>>>>
>>>>   I have been looking at this all afternoon, and tried to recreate
>>>> the problem by pasting similar unit test code into the
>>>> NHibernate.Test.VersionTest unit tests. (No luck yet.) Also, I tried
>>>> doing a "session.Clear()" before running this code, (which I thought
>>>> might solve the problem if there was stale data in the session) but it
>>>> had no effect.
>>>>
>>>>   I'm running out of ideas... does anyone have any thoughts on what
>>>> to look at next?
>>>>
>>>> Thanks,
>>>> Mike
>>>
>>> --
>>> You received this message because you are subscribed to the Google Groups 
>>> "nhusers" group.
>>> To post to this group, send email to [email protected].
>>> To unsubscribe from this group, send email to 
>>> [email protected].
>>> For more options, visit this group at 
>>> http://groups.google.com/group/nhusers?hl=en.
>>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups 
>> "nhusers" group.
>> To post to this group, send email to [email protected].
>> To unsubscribe from this group, send email to 
>> [email protected].
>> For more options, visit this group at 
>> http://groups.google.com/group/nhusers?hl=en.
>>
>>
>

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

Attachment: versionTypes.patch
Description: Binary data

Reply via email to