FindMatches returns strange value

Posts   
 
    
mag
User
Posts: 13
Joined: 28-Oct-2009
# Posted on: 04-Jan-2010 09:35:16   

Hi,

I'm using FindMatches to do an in-memory search. But there is a very strange behaviour...

Among other things an entity contains two datetime fileds (say: dt1 and dt2). In my implementation I use the collection of this entity. With the FindMatches method and the filter (bellow) I get the only entity which dt1 and dt2 fields both are null. Works as expected...

used filter:

IPredicateExpression filter = new PredicateExpression();
            filter.Add(new FieldCompareNullPredicate(myEntityFields.dt1));
            filter.AddWithAnd(new FieldCompareNullPredicate(MyEntityFields.dt2));

Now I do the following steps: - edit some fields of the entity - set the dt1 of the entity - create a new entity (dt1 and dt2 of the new entity are null) - doing FindMatches (with previous filter) now on the collections still works fine (the newly generated entity is the only one which will be found) - But after a save call of the collection, the first time FindMatches will be executed it delivers a wrong result. Instead of the newly generated entity it returns the old entity even though the dt1 field is set. The data in database is saved correct (the edited entity has some value in dt1 and the newly generated entity still have dt1 and dt2 set to null)

This appears only the first time after save would be called. A second call of FindMatches returns the correct entity. Is there some synchronisation problem in my code?

I'm using LLBLGen Pro version 2.6 Final, release on august 26th, 2009 using SelfServicing mode...

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 04-Jan-2010 09:49:47   

It's not clear from your words, whether just the FindMatches returns the wrong index, or when you catually grab this entity you'd find old data within.

So maybe a code snippet will clear things out.

mag
User
Posts: 13
Joined: 28-Oct-2009
# Posted on: 04-Jan-2010 10:22:12   

FindMatches delivers wrong index. if e.g. collection contains 3 entities and the third entity is the newly generaeted one (the only one with dt1 and dt2 fields set to null). FindMatches returns the second one (index 1), but this entity has some value in dt1 value...

I will try to post a image which shows that situation... one moment simple_smile

Image added... Hope that helps...

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 04-Jan-2010 15:38:46   

Would you please try the latest available runtime library build?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 04-Jan-2010 16:01:59   

Odd. Your field properties are not overridden in subclasses, they're normal entity field properties?

Frans Bouma | Lead developer LLBLGen Pro
mag
User
Posts: 13
Joined: 28-Oct-2009
# Posted on: 04-Jan-2010 16:16:35   

So I just tried with the newest runtime version, the same strange behaviour as before...

@Otis No there aren't any overrides...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 05-Jan-2010 10:19:11   

mag wrote:

So I just tried with the newest runtime version, the same strange behaviour as before...

@Otis No there aren't any overrides...

We'll see if we can reproduce this.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 05-Jan-2010 10:51:32   

reproduced confused


// Necessary related entities.
CustomerEntity newCustomer = EntityCreator.CreateNewCustomer(1);
newCustomer.TestRunId = _testRunID;
AddressEntity newAddress = EntityCreator.CreateNewAddress(1);
newAddress.TestRunId = _testRunID;
newCustomer.VisitingAddress = newAddress;
newCustomer.BillingAddress = newAddress;
EmployeeEntity newEmployee = new EmployeeEntity();
newEmployee.Name = "John Doe";
newEmployee.EmployeeSince = DateTime.Now;
newEmployee.TestRunId = _testRunID;

OrderCollection orders = new OrderCollection();
OrderEntity newOrder = new OrderEntity();
newOrder.Employee = newEmployee;
newOrder.Customer = newCustomer;
newOrder.OrderDate = DateTime.Now;
newOrder.ShippedDate = DateTime.Now;
newOrder.TestRunId = _testRunID;
orders.Add(newOrder);

newOrder = new OrderEntity();
newOrder.Employee = newEmployee;
newOrder.Customer = newCustomer;
newOrder.OrderDate = DateTime.Now;
newOrder.TestRunId = _testRunID;
orders.Add(newOrder);

Assert.AreEqual(5, orders.SaveMulti(true));

OrderCollection fetchedOrders = new OrderCollection();
fetchedOrders.GetMulti(OrderFields.CustomerId == newCustomer.CustomerId, 0, new SortExpression(OrderFields.OrderId | SortOperator.Ascending));
Assert.AreEqual(2, fetchedOrders.Count);

IPredicateExpression filter = new PredicateExpression();
filter.Add(new FieldCompareNullPredicate(OrderFields.ShippedDate));
filter.AddWithAnd(new FieldCompareNullPredicate(OrderFields.RequiredDate));

List<int> indexes = fetchedOrders.FindMatches(filter);
Assert.AreEqual(1, indexes.Count);
Assert.AreEqual(1, indexes[0]);

fetchedOrders[1].ShippedDate = DateTime.Now;
newOrder = new OrderEntity();
newOrder.Employee = newEmployee;
newOrder.Customer = newCustomer;
newOrder.OrderDate = DateTime.Now;
newOrder.TestRunId = _testRunID;
fetchedOrders.Add(newOrder);

indexes = fetchedOrders.FindMatches(filter);
Assert.AreEqual(1, indexes.Count);
Assert.AreEqual(2, indexes[0]);

Assert.AreEqual(2, fetchedOrders.SaveMulti(true));
indexes = fetchedOrders.FindMatches(filter);
Assert.AreEqual(1, indexes.Count);
Assert.AreEqual(2, indexes[0]);   // <<<<<<<<<<<<< reports 1, not 2.

Assert.AreEqual(2, fetchedOrders.DeleteMulti());
Assert.IsTrue(newEmployee.Delete());
Assert.IsTrue(newCustomer.Delete());
Assert.IsTrue(newAddress.Delete());

I'm baffled by this result, but I'm sure the debugger will show me what the cause is wink

(edit) looks like something to do with refetching the entities, as I can't reproduce it when I set a breakpoint in the debugger...

Frans Bouma | Lead developer LLBLGen Pro
mag
User
Posts: 13
Joined: 28-Oct-2009
# Posted on: 05-Jan-2010 11:23:59   

Same behaviour as I have... Hope you will find a solution.

Thanks for support...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 05-Jan-2010 11:36:06   

Found it. The cause is that you do a FindMatches on entities which are 'out of sync', i.o.w.: they're not refetched yet. The cause is the IsNull flag on the date field you set to a value. That field has its IsNull flag set to true, after SaveMulti, because it's set during fetch (and it represents that the original value read from the db is null). After the save this flag is still true and it is used in the FindMatches filter you use (as you compare to null).

In theory you shouldn't apply the filter right after the save, as the entities are 'out of sync', and first have to be refetched before you can proceed. There's a global flag which marks an entity as fetched after save. I looked into this and that method's code doesn't set the IsNull flag properly, it only sets the DbValue. The IsNull flag on a field doesn't look at DbValue, the field which contains the database field value, because that's unreliable in the situation where the field isn't a nullable type, but has a default value for the null state (through the typedefaultvalue provider): the flag then reports 'false' while the field is actually null.

Working with null values in-memory is a bit difficult, as an uninitialized object also contains nulls, but that's a different state than when you fetch a null from the db.

To work around this (which does cause a query per changed entity as it's refetched) you can use a DelegatePredicate on the property of the entity class instead, and compare that to null. Example:

filter = new PredicateExpression();
filter.Add(new DelegatePredicate(delegate(IEntityCore toExamine) { return !((OrderEntity)toExamine).ShippedDate.HasValue; }));
filter.Add(new DelegatePredicate(delegate(IEntityCore toExamine) { return !((OrderEntity)toExamine).RequiredDate.HasValue; }));

(of course, you can also use lambda expressions). You've to adjust the fields / entity type to your own type of course. Again, this will refetch the entities per-entity, as you touch the saved entities (so entity at index 1 and 2) and as they're out of sync, they're refetched, one at a time).

Frans Bouma | Lead developer LLBLGen Pro
mag
User
Posts: 13
Joined: 28-Oct-2009
# Posted on: 05-Jan-2010 11:50:08   

ou, sounds very complicated but with your code it works.

thank you for your help.