This project is read-only.

Performance issue: spurious queries for every resolved adapter

Apr 8, 2013 at 5:36 PM
Edited May 14, 2013 at 6:14 PM
In one of our domain object models the SectionAnswerItem class is a recursive structure, so that one instance is the parent of a number of child instances.

As per screenshot-1, below, you can see resolving the the child collection for a particular SectionAnswerItem causes EF to (correctly) queries the [Answers_Section_Item] table with the parent_answers_section_id as the foreign key.

If you then look at screenshot-2, below, you can see an additional call for each of the child objects, which is EF basically just checking that the Id just retrieved exists. We thus get an N+1 (or rather, 1+N) sort of problem.

We initially thought that this was some strange peculiarity of EF, but we then tracked the code down into the NO framework and found that those N calls are because of the way that EntityPersistor creating its PocoAdapters to wrap the domain objects.


The code in question is:
    public bool AreAnyObjectsWithKey(EntityOid eoid, Type type) {

        string entitySetName;

        LocalContext context = GetContext(type);

        List<KeyValuePair<string, object>> memberValueMap = GetMemberValueMap(type, eoid, out entitySetName);



        dynamic oq = context.CreateQuery(type, entitySetName);



        foreach (var kvp in memberValueMap) {

            string query = string.Format("it.{0}=@{0}", kvp.Key);

            oq = oq.Where(query, new ObjectParameter(kvp.Key, kvp.Value));

        }

        var result = (ObjectResult<int>)oq.SelectValue<int>(string.Format("Count(it.{0})", memberValueMap.First().Key)).Execute(MergeOption.NoTracking);

        return result.First() > 0;

    }


which is called from the following stack trace:
           NakedObjects.Persistor.Entity.dll!NakedObjects.EntityObjectStore.EntityObjectStore.AreAnyObjectsWithKey(NakedObjects.EntityObjectStore.EntityOid eoid, System.Type type) Line 684             C#
            NakedObjects.Persistor.Entity.dll!NakedObjects.EntityObjectStore.EntityObjectStore.EntityFrameworkKnowsObject(object domainObject) Line 748 + 0x25 bytes      C#

           NakedObjects.Persistor.Entity.dll!NakedObjects.EntityObjectStore.EntityIdentityMapImpl.GetAdapterFor(object domainObject) Line 37 + 0x18 bytes        C#

           NakedObjects.Core.dll!NakedObjects.Core.Persist.NakedObjectPersistorAbstract.GetAdapterFor(object obj) Line 104 + 0x1a bytes               C#

            NakedObjects.Core.dll!NakedObjects.Core.Persist.NakedObjectPersistorAbstract.AdapterForExistingObject(object domainObject, NakedObjects.Architecture.Adapter.IOid oid) Line 342 + 0xe bytes           C#

           NakedObjects.Core.dll!NakedObjects.Core.Persist.NakedObjectPersistorAbstract.AdapterFor(object domainObject, NakedObjects.Architecture.Adapter.IOid oid, NakedObjects.Architecture.Adapter.IVersion version) Line 139 + 0xe bytes C#

            NakedObjects.Core.dll!NakedObjects.Core.Persist.PersistorUtils.AdapterForPersistent(NakedObjects.Architecture.Adapter.IOid oid, object domainObject) Line 91 + 0x19 bytes C#

           NakedObjects.Persistor.Entity.dll!NakedObjects.EntityObjectStore.EntityObjectStore.LoadObject(object domainObject) Line 818 + 0x14 bytes      C#

            NakedObjects.Persistor.Entity.dll!NakedObjects.EntityObjectStore.EntityObjectStore.ObjectStateManagerChangedHandler(object obj, System.ComponentModel.CollectionChangeEventArgs args) Line 839 + 0x1f bytes  C#

           [External Code]               


In the ObjectStateManagerChangedHandler we know that the object exists ... the EntityPersistor is being called by EF, after all, and we also know this information at AdapterForExistingObject(), but by the time we're into the GetAdapterFor(...) we've forgotten this fact (ie we're calling some generic utility method) and so are making calls to check if EF knows the object or not.

So, I think that the EntityPersistor algorithm needs to be more sophisticated so that it doesn't make these spurious calls.

Note: this is on code running against NOF 5.0. It's possible that this has been changed in NOF 5.4, but I'm not aware that it did...

Dan




screenshot-1
screenshot-2
Apr 11, 2013 at 3:43 PM
This will take some investigation. It is unlikely that we would have created these apparently spurious requests without good reason. However, it is very possible that that reason has since disappeared i.e. this dates back to Naked Objects WPF (when the Entity Persistor was first created) - when we were working with 'long running contexts' - which posed horrible constraints on the operation. Now that we only operate with short running contexts, it is possible that there are a number of anachronisms, which could be safely removed. I've raised an issue to look into this.