Moving from EF to Fluent NHibernate: Memory Leaks,

2019-08-04 06:43发布

问题:

Good afternoon, I'm migrating a fairly large project over to Fluent NHibernate for use with mono. I've gotten most of the key functionality working well, however I'm having a memory issue.

Currently, this code is in two of my controllers. This, does not seem even slightly optimal. But I'm unsure where to put this.

 private static ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
           .Database(MySQLConfiguration.Standard.ConnectionString(
           c => c.FromConnectionStringWithKey("DashboardModels")
       ))
   .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Accounts>())
   .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Notes>())
    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Sales_Forecast>())
     .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ChangeLog>())
      .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Tasks>())

   .BuildSessionFactory();
    }
    ISessionFactory sessionFactory = CreateSessionFactory();

Most of my db calls are AJAX, with several firing at once. This leads me to believe that I'm creating too many sessions, which are not being released.

  public ActionResult ReadAccounts([DataSourceRequest] DataSourceRequest request)
    {
        DataSourceResult result;

        using (var session = sessionFactory.OpenStatelessSession())
        using (var tx = session.BeginTransaction())
        {
            var customers = from customer in session.Query<Accounts>().AsNoTracking()
                            where !customer.Deleted
                            select customer;
            result = customers.ToDataSourceResult(request);
            tx.Commit();
        }
    return Json(result, JsonRequestBehavior.AllowGet);


    }

Consider that I am using StatelessSessions for methods that only return data.

  public JsonResult GetNoteInfo(int id = 0)
    {
        Notes note;
        using (var session = sessionFactory.OpenStatelessSession())
        using (var tx = session.BeginTransaction())
        {
             note = (from notes in session.Query<Notes>().AsNoTracking()
                        where notes.Note_ID == id
                        select notes).FirstOrDefault();
             tx.Commit();
            return Json(JsonResponseFactory.SuccessResponse(note), JsonRequestBehavior.DenyGet);
        }
    }

As I mentioned, this is working, but how can I improve memory performance? There are about 5 methods similar to the bottom method that fire simultaneously, so anyone that has done this before if you can please advise on how to keep memory from quickly expanding I would be most grateful. Thank you for your time and happy coding!

回答1:

The controller will be instantiated for each request. It looks like you might be calling CreateSessionFactory() every time a controller is created. This is a very time-consuming call and it should typically be done only once for the application lifetime. A common way is to handle this in Global.asax.

As for the sessions, you have wrapped them in using statements, which will ensure they close the connections etc, so this part looks correct to me.



回答2:

This is how I personally solved my problem (with the help of DotJoe, Oskar Berggren, and others on this site)

Global.asax.cs

public static ISessionFactory SessionFactory =
         SessionProvider.BuildSessionFactory();

Controller

public ActionResult ReadAccounts([DataSourceRequest] DataSourceRequest request)
{
    DataSourceResult result;

    using(ISession session = MvcApplication.SessionFactory.OpenSession())
    {
        using (ITransaction tx = session.BeginTransaction())
        {
            var customers = session.Query<Accounts>().Where(a => a.Deleted == false).AsNoTracking();
            //from customer in session.Query<Accounts>().AsNoTracking()
            //                where !customer.Deleted
            //                select customer;
            result = customers.ToDataSourceResult(request);
            tx.Commit();
        }
    }
    return Json(result, JsonRequestBehavior.AllowGet);
}

I then created a class in a folder called Utilities:

SessionFactory.cs

public class SessionProvider  
{
 public static ISessionFactory BuildSessionFactory()
 {
 return Fluently.Configure()
      .Database(MySQLConfiguration.Standard.ConnectionString(
      c => c.FromConnectionStringWithKey("DashboardModels")
  ))
 .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Accounts>())
 .BuildSessionFactory();

 }
}

I hope this code can help anyone else who needs to convert from EF to NHibernate in a pinch.

This has kept my memory management in a pretty large application with 145 users to a little over 450,000kb. (down from 600,000kb using EF) a testament to the scalability of NHibernate.

NHProf is highly recommended as well. It has great suggestions and will provide resources for your specific problem.

If you're going to use Mono, This is the way to go in my opinion.