Closed
Description
Investigating a memory leak in an app, it was evident that the NHibernate.Engine.Query.QueryPlanCache was holding on to references to entities via instances of NHibernate.Param.NamedParameter. This only happens if the linq query expression embeds the entity itself as a parameter.
Below is a complete example which demonstrates this behaviour, note that only the first time a unique query is used is the entity pinned, see var first_reference_is_not_null = firstReference.Target;
:
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Mapping;
using NHibernate;
using NHibernate.Dialect;
using NHibernate.Tool.hbm2ddl;
public class programm
{
public static void Main()
{
WeakReference sessionReference = null;
WeakReference firstReference = null;
WeakReference secondReference = null;
new Action(() =>
{
var session = ConfigureSessionFactory();
var first = new Test() { Id = 1 };
var second = new Test() { Id = 2 };
var f = session.Query<Test>().FirstOrDefault(f => f == first);
var s = session.Query<Test>().FirstOrDefault(f => f == second);
sessionReference = new WeakReference(session, true);
firstReference = new WeakReference(first, true);
secondReference = new WeakReference(second, true);
session.Dispose();
})();
GC.Collect();
GC.WaitForPendingFinalizers();
var session_reference_is_null = sessionReference.Target;
var first_reference_is_not_null = firstReference.Target;
var second_reference_is_null = secondReference.Target;
}
private static ISession ConfigureSessionFactory()
{
var configuration = Fluently.Configure().Database(SQLiteConfiguration.Standard.InMemory).Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<Test>();
});
configuration.ExposeConfiguration(c => SchemaMetadataUpdater.QuoteTableAndColumns(c, new MsSql2012Dialect()));
var config = configuration.BuildConfiguration();
var factory = config.BuildSessionFactory();
var session = factory.OpenSession();
var wr = new StringWriter();
new SchemaExport(config).Execute(true, true, false, session.Connection, wr);
var t = wr.ToString();
return session;
}
public class TestMap : ClassMap<Test>
{
public TestMap()
{
Id(x => x.Id).GeneratedBy.Assigned();
}
}
public class Test
{
public virtual int Id { get; set; }
}
}