New Posts New Posts RSS Feed: ExecuteAsync leaking memory?
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

ExecuteAsync leaking memory?

 Post Reply Post Reply
Author
sarmaad View Drop Down
Newbie
Newbie


Joined: 18-Jul-2010
Location: Sydney
Posts: 15
Post Options Post Options   Quote sarmaad Quote  Post ReplyReply Direct Link To This Post Topic: ExecuteAsync leaking memory?
    Posted: 30-Sep-2010 at 5:31am
I have been memory profiling my WPF application and came across this behaviour...

when I execute this statement:

_repository.GlobalManager
        .Contacts
        .AddIncludePaths("ContactAddresses")
        .Where(col => col.ContactID == contactId)
        .With(QueryStrategy.DataSourceThenCache)
        .ExecuteAsync()
it creates a reference leading back to my ViewModel that will stop the GC from doing its job...
However, Execute() works as expected.
not sure if I am doing something wrong or I am not understanding the proper use of ExecuteAsync.
I have attached a Object Retention Graph to clarify the question...
http://www.sarmaad.com/files/cc_org_executeasync.jpg
any feedback would be appreciated.
EDIT:
after some more testing... I was mistakenly thinking its holding the ViewModel. 
Execute/FirstOrNullEntity produced exactly the same results!



Edited by sarmaad - 07-Oct-2010 at 12:50pm
Back to Top
sarmaad View Drop Down
Newbie
Newbie


Joined: 18-Jul-2010
Location: Sydney
Posts: 15
Post Options Post Options   Quote sarmaad Quote  Post ReplyReply Direct Link To This Post Posted: 04-Oct-2010 at 6:07am
I have been able to solve this problem...

After a long testing and trial and error, I have found this interesting behaviour...

maybe someone at Ideablade can shed some light on it...

In my app, I have a global Entity Manager that holds all entities for the application.
This global manager is exposed as a property in which the class is exported using MEF.

[Export(typeof(IEntityRepository))]
class EntityRepository : IEntityRepository
{
public EntityModelManager GlobalManager { get; private set; }
public EntityModelManager CreateManager()
{
return new EntityModelManager(GlobalManager, true, DataSourceExtension, GlobalManager.EntityServiceOption);
}

 [.... other properties/methods emitted ....]

}

when I open a record to perform edit operations, I use a localized manager. 
the local manager will pull down from global manager any entities it needs to perform its operations.
this is happening via injecting the IEntityRepository at the constructor level of the consuming class.

[ExportViewModel("CRM.EditContact")]
[PartCreationPolicy(CreationPolicy.NonShared)]
class CustomerEditViewModel
{
IEntityRepository _repository;
EntityModelManager _manager;

[ImportingConstructor]
public ContactEditViewModel(IEntityRepository repository)
{
            _repository = repository;
            //local manager
            _manager = _repository.CreateManager();
}

when the form is shown, I had another method that preloaded the cache using AsyncParallelTask and loaded the entity by ID.

this is how I retrieved the entity from global manager to local manager

var query = new EntityQuery<Contact>()
.AddIncludePaths("ContactAddresses")
.Where(col => col.ContactID == contactId)
.With(QueryStrategy.DataSourceThenCache);

_repository.GlobalManager
.ExecuteQueryAsync(
                    query,
                    opt =>
[code emitted] 

var entityGraph = _repository.GlobalManager.FindEntityGraph(roots, spans, EntityState.Unchanged);
_manager.ImportEntities(entityGraph, MergeStrategy.OverwriteChanges);

[code emitted] 
}
                    tag);

as you can see, I have executed the query against the global manager and imported the resulted entity graph to the local manager.

when the form is closed and all variables, fields, properties cleaned up, I still have a reference linking to the ViewModel and not allowing the GC from cleaning up the ViewModel, causing the memory leak.

now, How I solved the issue is by changing the last method to execute the query against the local manager and not the global.

_manager
.ExecuteQueryAsync(
                    query,
                    opt =>
[code emitted] 

//no need to get the graph or import any entities
//var entityGraph = _repository.GlobalManager.FindEntityGraph(roots, spans, EntityState.Unchanged);
//_manager.ImportEntities(entityGraph, MergeStrategy.OverwriteChanges);

[code emitted] 
}
                    tag);

what really interesting, why my previous code, the EntityQuery maintained a link to the query but the preload cache method with utilize AsyncParallelTask worked perfectly without maintainig any link?

here is the preload cache method:
int i = 0;
var task = AsyncParallelTask.Create()
.AddAsyncQuery(++i, x => _repository.GlobalManager.AddressTypes,
opt => _manager.ImportEntities(opt.Results, MergeStrategy.OverwriteChanges));
task.Execute(opt =>
{
[... error checks emitted ...]

_manager.AddressTypes.ForEach(AddressTypes.Add);

[code emitted]
});

I hope this could help someone else in similar situation and hopefully I will get some feedback from Ideablade shedding some light on this..

Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down