That is because the Seed should return a valid value and not a null.
For integer based entities the Seed returns 1, that mean that a null
can't be the previous value stored for version. A null can be a value
for "unsaved" but not the actual value for a stored state.

--
Fabio Maulo


El 17/11/2010, a las 20:09, Mike Pontillo <[email protected]> escribió:

>   I have confirmed that this is the problem. (there "where" clause on
> the update can't handle a NULL version property) I see that in another
> part of AbstractEntityPersister.cs, this case is handled; for example:
>
>        updateBuilder.AddWhereFragment(_propertyColumnNames[k] + " is
> null");
>
>   I tried changing the code to handle this case, but the change
> quickly got much larger than I wanted. There seems to be the
> assumption (in several places) that the version property can never be
> null, including AbstractEntityPersister.GenerateUpdateString(), which
> is called at init time and [seems to?] cache the SQL for doing an
> update. This includes the "where" clause which assumes that the
> version property is not null.
>
>   There is also a call to SetVersionColumn() in the
> UpdateLockingStrategy class (GenerateLockString()) that I'm not sure
> what to do with, as this would also depend on the value of the version
> property.
>
>   I am guessing it was done this way for performance reasons? If
> every update needed to check for this obscure corner case, it might
> become significant.
>
> Regards,
> Mike
>
>
> On Nov 17, 11:49 am, Mike Pontillo <[email protected]> wrote:
>> Thanks,
>>
>>    Yes, it seems I will need a custom type, if only for the null-safe
>> .Next() method. I looked at the interface definition and didn't see
>> anything else that I really need to override.
>>
>>    As for multiple unsaved-values, I checked the IsUnsaved() method in
>> the VersionValue class and noticed that it checks for either null, or
>> the value. So I think that case is covered if I set unsaved-value="0"
>> in the XML.
>>
>>    Now the problem I am running into is different: the version update
>> fails. When I show the SQL I see something like this:
>>
>> NHibernate: UPDATE dbo.[EXAMPLE] SET [VERSION] = @p0 WHERE [ID] = @p1
>> AND [VERSION] = @p2;@p0 = 1 [Type: Int32 (0)], @p1 = 42 [Type: Decimal
>> (0)], @p2 = NULL [Type: Int32 (0)]
>>
>>    ... then NHibernate notices that while it expected one row to be
>> updated, zero rows were updated. (see stack trace below my signature)
>>
>>    I'm not sure if this is the real problem, but shouldn't the UPDATE
>> in this case be generated with "VERSION is NULL", not a "VERSION =
>> <some-value>"? In the SQL I see VERSION = @p2, where @p2 is the NULL
>> Int32 object. I thought that the NullSafeSet() was intended to handle
>> this case (called in ForceVersionIncrement() in
>> AbstractEntityPersister) but I wasn't sure how this happens. (Does
>> .NET take care of changing this to an "is NULL" statement?)
>>
>>    I ran the same SQL manually and it updates the row, but only if I
>> write "is null".
>>
>> Regards,
>> Mike
>>
>> NHibernate.StaleObjectStateException : Row was updated or deleted by
>> another transaction (or unsaved-value mapping was incorrect):
>> [MyCompany.Example#42]
>>   ----> NHibernate.StaleStateException : Unexpected row count: 0; expected: 1
>> at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32
>> rows, Object id, Int32 tableNumber, IExpectation expectation,
>> IDbCommand statement) in AbstractEntityPersister.cs: line 2174
>> at 
>> NHibernate.Persister.Entity.AbstractEntityPersister.ForceVersionIncrement(Object
>> id, Object currentVersion, ISessionImplementor session) in
>> AbstractEntityPersister.cs: line 1618
>> at 
>> NHibernate.Event.Default.AbstractLockUpgradeEventListener.UpgradeLock(Object
>> entity, EntityEntry entry, LockMode requestedLockMode,
>> ISessionImplementor source) in AbstractLockUpgradeEventListener.cs:
>> line 64
>> at NHibernate.Event.Default.DefaultLockEventListener.OnLock(LockEvent
>> event) in DefaultLockEventListener.cs: line 55
>> at NHibernate.Impl.SessionImpl.FireLock(LockEvent lockEvent) in
>> SessionImpl.cs: line 2470
>> at NHibernate.Impl.SessionImpl.Lock(Object obj, LockMode lockMode) in
>> SessionImpl.cs: line 776
>> --StaleStateException
>> at 
>> NHibernate.AdoNet.Expectations.BasicExpectation.VerifyOutcomeNonBatched(Int32
>> rowCount, IDbCommand statement) in Expectations.cs: line 33
>> at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32
>> rows, Object id, Int32 tableNumber, IExpectation expectation,
>> IDbCommand statement) in AbstractEntityPersister.cs: line 2163
>>
>> On Wed, Nov 17, 2010 at 10:54 AM, Fabio Maulo <[email protected]> wrote:
>>> ah... The Versioning is not the place of the modification, instead, the
>>> right place is the implementation of IVersionType inside Int32Type.
>>> btw... a IUserVersionType would be perfect for your case.
>>
>>> On Wed, Nov 17, 2010 at 3:44 PM, Fabio Maulo <[email protected]> wrote:
>>
>>>> The implementation of a custom type is not an overkill, instead it is just
>>>> the way NH gives you to define what is correct/intelligent for you.
>>>> For me Int32 is more than enough and its default "unsaved-value" as zero
>>>> is more than enough. You are looking for a version with two possible
>>>> unsaved-value ('null' and zero)
>>>> On Tue, Nov 16, 2010 at 9: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
>>
>> ...
>>
>> read more »
>
> --
> 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.

Reply via email to