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..