- Home
- LLBLGen Pro
- Bugs & Issues
Not updating nullable Int column to null
Joined: 19-Jan-2011
Hi, I am unable to update a nullable int column to null, it updates to any value other than null.
I confirmed that my database field is nullable as well. I stepped into the code and I send the correct value to the adapter(null).
The generated sql does not include the column when the value is null.(checked with profiler) It includes the column on the sql statement when the value is not null.
I am using LLblgen 3.1 .Net 4.0 C# using Ria Services, the entity has a collection of entities with a composition attribute for RIA. I do not implement the integrated crud operations on the LLBLGenProDomainService2 base class, I call my own bol(all works ok except for this).
Version 3.1.0.0 SD.LLBLGen.Pro.ORMSupportClasses.NET20, v2.0.50727 SD.LLBLGen.Pro.RiaSupportClasses.dll, v4.0.30319
MS SQL 2008
Thanks Andres (customer number 17303)
This is what I see on the server side for that entity field(field value is null)
_actAsDerivedTableField false bool
_alias null string
_beginEditIsChangedState false bool
_currentValue null object
_dbValue null object
_derivedTableTargetName null string
_dynamicUsageInfo null SD.LLBLGen.Pro.ORMSupportClasses.EntityFieldCore.DynamicFieldInfo
+ _fieldInfo {SD.LLBLGen.Pro.ORMSupportClasses.FieldInfo}
SD.LLBLGen.Pro.ORMSupportClasses.IFieldInfo {SD.LLBLGen.Pro.ORMSupportClasses.FieldInfo}
+ _inheritanceFieldInfo {SD.LLBLGen.Pro.ORMSupportClasses.EntityFieldCore.InheritanceFieldInfo}
SD.LLBLGen.Pro.ORMSupportClasses.EntityFieldCore.InheritanceFieldInfo
_isChanged false bool
_isNull false bool
_objectAlias "" string
_originalValue null object
ActAsDerivedTableField false bool
ActualContainingObjectName "DocTypeAttEntity" string
AggregateFunctionToApply None SD.LLBLGen.Pro.ORMSupportClasses.AggregateFunction
Alias "GridDisplayPosition" string
ContainingObjectName "DocTypeAttEntity" string
CurrentValue null object
CurrentValueInternal null object
+ DataType {Name = "Nullable`1" FullName = "System.Nullable`1[
[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
System.Type {System.RuntimeType}
DbValue null object
DbValueInternal null object
ExpressionToApply null SD.LLBLGen.Pro.ORMSupportClasses.IExpression
FieldIndex 3 int
[colorvalue="FF0000"][b]IsChanged false[/b][/color] bool
IsForeignKey false bool
IsInMultiTargetEntity false bool
IsNull false bool
[colorvalue="FF0000"][b]IsNullable true[/b][/color] bool
IsOfEnumDataType false bool
IsPrimaryKey false bool
IsReadOnly false bool
+ LinkedSubTypeFields Count = 0
System.Collections.Generic.List<SD.LLBLGen.Pro.ORMSupportClasses.IEntityFieldCore>
LinkedSuperTypeField null SD.LLBLGen.Pro.ORMSupportClasses.IEntityFieldCore
MaxLength 0 int
Name "GridDisplayPosition" string
ObjectAlias "" string
Precision 10 byte
+ RealDataType {Name = "Int32" FullName = "System.Int32"} System.Type {System.RuntimeType}
Scale 0 byte
SD.LLBLGen.Pro.ORMSupportClasses.IEntityFieldCoreInternal.DerivedTableTargetingFieldName null string
SD.LLBLGen.Pro.ORMSupportClasses.IEntityFieldCoreInternal.IsDerivedTableTargetingField false bool
+ Static members
I was able to see the original value of the entity using ChangeSet and it is clearly the original value. DocTypeAttEntity original = this.ChangeSet.GetOriginal<DocTypeAttEntity>(docTypeAttEntity);
the field GridDisplayPosition is an int = 3; so there must be a problem deserializing the entity. I will implement a fix on my side but that should be fixed soon. Mark fields changed using the LLBLGenProDomainService2 class(in this case the nullable of int).
Joined: 19-Jan-2011
This is the generic workaround in case some one is looking for a hack while this gets fixed. Needs testing.
This will mark all the entities in the change set(includes related entities).
public void UpdateEntity(DocTypeEntity entity)
{
ChangeOperation opMain = ChangeSet.GetChangeOperation(entity);
if (opMain == ChangeOperation.Update)
entity.IsNew = false;
if (entity.DocTypeAtts != null)
entity.DocTypeAtts.RemovedEntitiesTracker = new EntityCollection<DocTypeAttEntity>();
foreach (DocTypeAttEntity docTypeAttEntity in this.ChangeSet.GetAssociatedChanges<DocTypeEntity, EntityCollection<DocTypeAttEntity>>(entity, p => p.DocTypeAtts))
{
ChangeOperation op = this.ChangeSet.GetChangeOperation(docTypeAttEntity);
switch (op)
{
case ChangeOperation.Delete:
docTypeAttEntity.IsDirty = false;
docTypeAttEntity.IsNew = false;
entity.DocTypeAtts.RemovedEntitiesTracker.Add(docTypeAttEntity);
//entity.DocTypeAtts.Remove(docTypeAttEntity);//ria removes the entity so i can not remove it again
break;
case ChangeOperation.Insert:
docTypeAttEntity.IsNew = true;
break;
case ChangeOperation.None:
docTypeAttEntity.IsNew = false;
docTypeAttEntity.IsDirty = false;
break;
case ChangeOperation.Update:
MarkFieldsChanged<DocTypeAttEntity>(docTypeAttEntity, this.ChangeSet);
docTypeAttEntity.IsNew = false;
break;
default:
docTypeAttEntity.IsDirty = false;
docTypeAttEntity.IsNew = false;
break;
}
}
warning //Hack, entities are not propertly marked as Changed, this should be removed after a better fix is available
MarkFieldsChangedOnAllEntities(this.ChangeSet);
CallContext cc = new CallContext(Membership.GetUser().ProviderUserKey.ToString());
BusinessObjectResult<DocTypeEntity> result = bol.SaveAndGetEntity(cc, entity);
if (result.ResultType == BusinessObjectResultType.Success)
entity = result.ResultObject;
//reserach why this is serialized back by ria services, I am currently taking care of the work on my bol, so this is out of sync
foreach (ChangeSetEntry item in this.ChangeSet.ChangeSetEntries)
{
((EntityBase2)item.Entity).Fields.State = SD.LLBLGen.Pro.ORMSupportClasses.EntityState.Fetched;
}
}
public void MarkFieldsChangedOnAllEntities(ChangeSet changeSet)
{
foreach (ChangeSetEntry item in changeSet.ChangeSetEntries)
{
Type entityType = item.Entity.GetType();
MethodInfo method = GetType().GetMethod("MarkFieldsChanged");
MethodInfo genericMethod = method.MakeGenericMethod(new Type[] { entityType });
object[] parameters = new object[2];
parameters[0] = item.Entity;
parameters[1] = changeSet;
genericMethod.Invoke(this, parameters);
}
}
public void MarkFieldsChanged<EntityType>(object entity, ChangeSet changeSet)
where EntityType : EntityBase2
{
EntityType original = changeSet.GetOriginal<EntityType>((EntityType)entity);
if (original != null)
foreach (IEntityField2 item in ((EntityType)entity).Fields)
{
object originalPropertyVal = original.GetCurrentFieldValue(((SD.LLBLGen.Pro.ORMSupportClasses.EntityFieldCore)(item)).FieldIndex);
object newPropertyVal = ((SD.LLBLGen.Pro.ORMSupportClasses.EntityFieldCore)(item)).CurrentValue;
if (!item.IsChanged && ((originalPropertyVal != null && newPropertyVal == null) || (originalPropertyVal == null && newPropertyVal != null)
|| (originalPropertyVal != null && !originalPropertyVal.Equals(newPropertyVal))
|| (newPropertyVal != null && !newPropertyVal.Equals(originalPropertyVal)))
)
{
item.IsChanged = true;
}
}
}
Hi anlebu, Sorry but I don't understand your trace in the first post neither your workaround. I don't think there is anything to fix IMHO. If you could show us a simple code snippet that reproduce what you think is a bug, we can look into it to fix it. Normally if a null value is not set is because the original value (DBValue) is null as well, so no change is detected. Please double check this.
Joined: 19-Jan-2011
Hi Daelmo, please disregard that trace and just read the rest of the email.
these are the steps to reproduce it (no code to make it easier to understand)
-Create a DomainService class using the Base Class provided by llblgen 3.1, it provides crud operations for an Entity with a nullable int column.
-on Silverlight 4 client, get an entity with the nullable int field set to 3(using the generated DomainContext)
-on Silverlight 4 client, update the entity nullable field to null and then call the update operation on the generated DomainContext
-at this point the DomainService gets the entity wich has the correct nullable field value(null) but the field is not marked as changed.
please let me know if you still don't understand so I can send you more details.
David wrote:
Normally if a null value is not set is because the original value (DBValue) is null as well, so no change is detected. Please double check this.
Would the value be set to null, if the database field has another value but null?
Joined: 19-Jan-2011
No, the field value is 3 on the database and is not set to null. My previous threads explain that in detail. my work around has access to the property values before and after the changed are done on the generated Ria Entities using the ChangeSet on the DomainService, this is how i marked the fields as changed.
It seems that the logic to mark the Fields as changed is natively on the llblgen entities, since Ria generates the client side entities, this logic is removed from here, therefore it needs to be implemented after the deserialization of the entity.
I am not sure at what level the two entities need to be sync, but my guess is that it should happen between the deserialization and before the entity is passed to the domain service, and I don't think this should be done by the llblgen customer since Ria Services are supported now, thats why I got 3.1.
I appreciate your time, and as you know I have a Hack work around so this is not stopping me.
The version number you gave is the .net version number. Please right-click the assembly dll in windows explorer -> properties -> version tab.
Anyway, on August 12th, we fixed a bug in the RIA services dll, as nullable fields didn't get the roundtriporiginal attribute. Please check whether you're using a build after that date (e.g. get the latest build from the website). When you update the dll, you have to rebuild the solution to make it take effect, as wcf ria services generates code when building.
It might be you're using an early beta build of the wcf ria services which we shipped for 3.0?. If you're using vanilla v3.1 RTM, you should have the right dll and the right code.
Joined: 19-Jan-2011
I am using SD.LLBLGen.Pro.RiaSupportClasses.dll (file version 3.1.11.207, Date Modified 2/10/2011 10:32 AM)
It is both in my dll source folder and on the webapp/bin
As I described previously I am getting the original value using the ChangeSet, meaning that the column does have the roundtriporiginal, so that seems to be unrelated to this issue.
Thanks
I can't reproduce it.
[TestMethod]
[Asynchronous]
[Tag("UpdateAddress")]
public void UpdateAddressTestRunIdAfterFetch()
{
var svc = new UnitTesterSvc();
Exception error = null;
int addressId = -1;
svc.InsertAddressSimple(
x =>
{
error = x.Error;
addressId = x.Value;
}, null);
this.EnqueueConditional(() => addressId > 0);
this.EnqueueCallback
(
() => { if(error != null) { throw error; } },
() => Assert.IsTrue(addressId > 0, "AddressId should be set to a value > 0")
);
IEnumerable<AddressEntity> results = null;
this.EnqueueCallback
(
() =>
{
var addressQ = svc.GetAddressesQuery(addressId, false);
svc.Load(addressQ,
o =>
{
results = o.Entities;
error = o.Error;
}, null);
}
);
this.EnqueueConditional(() => results != null);
bool saveCompleted = false;
this.EnqueueCallback
(
() =>
{
Assert.AreEqual(1, results.Count(), "result count should be 1");
var address = results.First();
Assert.AreEqual(addressId, address.AddressId, "addressid doesn't match");
address.TestRunId = null;
},
() =>
{
svc.SubmitChanges(x => { saveCompleted = true; }, null);
}
);
this.EnqueueConditional(() => saveCompleted);
results = null;
this.EnqueueCallback
(
() =>
{
var addressQ = svc.GetAddressesQuery(addressId, false);
svc.Load(addressQ,
o =>
{
results = o.Entities;
error = o.Error;
}, null);
}
);
this.EnqueueConditional(() => results != null);
this.EnqueueCallback
(
() =>
{
Assert.AreEqual(1, results.Count(), "result count should be 1. After save");
var address = results.First();
Assert.AreEqual(addressId, address.AddressId, "addressid doesn't match. After save");
Assert.IsNull(address.TestRunId, "TestRunId should be null");
},
() =>
{
svc.SubmitChanges(x => { saveCompleted = true; }, null);
}
);
this.EnqueueTestComplete();
}
I tested it with a nullable Guid, (TestRunId in this example), and it is inserted with a value, then it's updated with 'null' and persisted again and then checked whether it is indeed null.
I.o.w.: could you provide us with a ready to run SIMPLE (!) reprocase on adventureworks or northwind? thanks
Joined: 19-Jan-2011
Thanks Otis, I will do it as soon as I get a break(this may take a while). I appreciate your time and effort.
By the way, I did not see any non null value assignment to the field TestRunId, the test should have saved a non null value on TestRunId then fetch then update TestRunId to null.
Is this "var svc = new UnitTesterSvc();" a new instance of the DomainContext or is it a new DomainService, because the problem needs to be tested going across to the DomainContext and coming back to the DomainService, I know the DomainService correctly updates an entity field that is marked as changed.
Thanks.
anlebu wrote:
Thanks Otis, I will do it as soon as I get a break(this may take a while). I appreciate your time and effort.
By the way, I did not see any non null value assignment to the field TestRunId, the test should have saved a non null value on TestRunId then fetch then update TestRunId to null.
the InsertAddressSimple method does that, it's on the service, it inserts a valid TestRunId (I checked )
Is this "var svc = new UnitTesterSvc();" a new instance of the DomainContext or is it a new DomainService, because the problem needs to be tested going across to the DomainContext and coming back to the DomainService, I know the DomainService correctly updates an entity field that is marked as changed. Thanks.
It's a new instance of the DomainContext. So you mean: the domain context has to be destroyed after initial save, then create a new one, then update the entity for testing?
Joined: 19-Jan-2011
It's a new instance of the DomainContext. So you mean: the domain context has to be destroyed after initial save, then create a new one, then update the entity for testing?
No, that test is correct then. I will look into the test case later. Thanks