C# 了解NHibernate IUserType及其为什么会留在内存中

C# 了解NHibernate IUserType及其为什么会留在内存中,c#,asp.net,nhibernate,memory-leaks,fluent-nhibernate,C#,Asp.net,Nhibernate,Memory Leaks,Fluent Nhibernate,为了尝试找出所有内存被占用的位置,我已经分析了一个web应用程序好几天了,它开始显示NHibernate有点像一个内存饥饿的野兽——但我真的只剩下最后一件事了: 自定义类型似乎保存在内存中,这导致了一点内存泄漏 **运行下面的代码后,我分析了内存。在它的左边是一个UserConfig实例(用户实例已经被GC’d了)——因为用户已经离开了,所以我希望UserConfig的一个实例也离开了。“实例保留图”显示: 用户配置 | NHibernate.Type.CustomType | NHiberna

为了尝试找出所有内存被占用的位置,我已经分析了一个web应用程序好几天了,它开始显示NHibernate有点像一个内存饥饿的野兽——但我真的只剩下最后一件事了:

自定义类型似乎保存在内存中,这导致了一点内存泄漏

**运行下面的代码后,我分析了内存。在它的左边是一个UserConfig实例(用户实例已经被GC’d了)——因为用户已经离开了,所以我希望UserConfig的一个实例也离开了。“实例保留图”显示:

用户配置 | NHibernate.Type.CustomType | NHibernate.Type.IType[] | NHbernate.Persister.Entity.SingleTableEntityTyperMaster | 系统。集合。。。。。 | 系统。集合。。。。。 | NHibernate.Impl.SessionFactoryImpl | 会话源

我在这里包含了一些代码来演示它(请不要对代码本身发表评论-我写这篇文章纯粹是为了测试这个问题,它不应该是生产就绪的代码)

我在模块的末尾添加了一个GC.Collect(),因此内存快照中的任何内容都是问题的一部分

我只包含了代码的相关部分(如果你喜欢整个项目,请发电子邮件给我)

不使用CustomType不是一个选项

会话工厂…

            SessionFactory = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008.ConnectionString(ConfigurationManager.ConnectionStrings["ConnectionString"].ToString()))
                .Mappings(x => GetFluantMappings(x))
                .ExposeConfiguration(c =>
                                         {
                                             c.SetProperty("generate_statistics", "true");
                                             c.SetProperty("current_session_context_class", contextClass);
                                             c.SetProperty("cache.use_second_level_cache", "false");
                                             c.SetProperty("cache.use_query_cache", "false");

                                         })
                .BuildSessionFactory();
        }
        catch(FluentConfigurationException ex)
        {
            throw new SessionSourceException("Error initializing session factory", ex);
        }
每个请求的会话httpmodule

    private static void BeginRequest(object sender, EventArgs e)
    {
        ISession session = SessionSource.Instance.SessionFactory.OpenSession();
        ManagedWebSessionContext.Bind(HttpContext.Current, session);
    }

    private static void EndRequest(object sender, EventArgs e)
    {
        ISession session = ManagedWebSessionContext.Unbind(HttpContext.Current, SessionSource.Instance.SessionFactory);

        if (session == null) return;

        session.Close();
        session.Dispose();
        GC.Collect();
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        int userId = CreateUser(SessionSource.Instance.SessionFactory);
        GetUser(SessionSource.Instance.SessionFactory, userId);
    }

    private static void GetUser(ISessionFactory sessionFactory, int userId)
    {
        ISession session = sessionFactory.GetCurrentSession();
        User user = session.Get<User>(userId);
        //User user = session.Query<User>().Single(u => u.Id == userId);
    }

    private static int CreateUser(ISessionFactory sessionFactory)
    {
        User user = new User();
        user.Created = DateTime.Now;
        user.Email = "first@lastname.com";
        user.Enabled = true;
        user.FirstName = "first name";
        user.LastName = "last name";
        user.Password = "password";
        user.ScreenName = "firstname lastname";
        ISession session = sessionFactory.GetCurrentSession();
        session.SaveOrUpdate(user);
        return user.Id;
    }
aspx页面

    private static void BeginRequest(object sender, EventArgs e)
    {
        ISession session = SessionSource.Instance.SessionFactory.OpenSession();
        ManagedWebSessionContext.Bind(HttpContext.Current, session);
    }

    private static void EndRequest(object sender, EventArgs e)
    {
        ISession session = ManagedWebSessionContext.Unbind(HttpContext.Current, SessionSource.Instance.SessionFactory);

        if (session == null) return;

        session.Close();
        session.Dispose();
        GC.Collect();
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        int userId = CreateUser(SessionSource.Instance.SessionFactory);
        GetUser(SessionSource.Instance.SessionFactory, userId);
    }

    private static void GetUser(ISessionFactory sessionFactory, int userId)
    {
        ISession session = sessionFactory.GetCurrentSession();
        User user = session.Get<User>(userId);
        //User user = session.Query<User>().Single(u => u.Id == userId);
    }

    private static int CreateUser(ISessionFactory sessionFactory)
    {
        User user = new User();
        user.Created = DateTime.Now;
        user.Email = "first@lastname.com";
        user.Enabled = true;
        user.FirstName = "first name";
        user.LastName = "last name";
        user.Password = "password";
        user.ScreenName = "firstname lastname";
        ISession session = sessionFactory.GetCurrentSession();
        session.SaveOrUpdate(user);
        return user.Id;
    }
受保护的无效页面加载(对象发送方,事件参数e)
{
int userId=CreateUser(SessionSource.Instance.SessionFactory);
GetUser(SessionSource.Instance.SessionFactory,用户ID);
}
私有静态void GetUser(ISessionFactory sessionFactory,int userId)
{
ISession session=sessionFactory.GetCurrentSession();
User=session.Get(userId);
//User User=session.Query().Single(u=>u.Id==userId);
}
私有静态int CreateUser(ISessionFactory sessionFactory)
{
用户=新用户();
user.Created=DateTime.Now;
user.Email=”first@lastname.com";
user.Enabled=true;
user.FirstName=“first name”;
user.LastName=“last name”;
user.Password=“Password”;
user.ScreenName=“firstname lastname”;
ISession session=sessionFactory.GetCurrentSession();
session.SaveOrUpdate(用户);
返回user.Id;
}
用户类和用户映射

public class User
{
    public virtual int Id { get; set; }
    public virtual string Email { get; set; }
    public virtual string ScreenName { get; set; }
    public virtual string Password { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual bool Enabled { get; set; }
    public virtual DateTime Created { get; set; }
    public virtual DateTime? LastLogin { get; set; }
    public virtual DateTime? LastActivity { get; set; }
    public virtual UserConfig Config { get; set; }
}

[Serializable]
public class UserConfig : List<string>, IUserType
{
    public UserConfig() { }

    public UserConfig(IEnumerable<string> items) : base(items)
    {

    }

    #region IUserType Members

    public object Assemble(object cached, object owner)
    {
        return DeepCopy(cached);
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Disassemble(object value)
    {
        return DeepCopy(value);
    }

    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        return x.Equals(y);
    }

    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    public bool IsMutable
    {
        get { return false; }
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        object obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);
        if (obj == null)
        {
            return new UserConfig();
        }

        return XmlSerialiser.FromXml<UserConfig>(obj as string);
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var parameter = (IDataParameter)cmd.Parameters[index];
        if (value == null)
        {
            parameter.Value = DBNull.Value;
        }
        else
        {
            parameter.Value = XmlSerialiser.ToXml<UserConfig>(value);
        }
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    [XmlIgnore]
    public Type ReturnedType
    {
        //the .Net type that this maps to
        get { return typeof(UserConfig); }
    }

    [XmlIgnore]
    public SqlType[] SqlTypes
    {
        //the sql type that this maps to
        get { return new[] { SqlTypeFactory.GetString(int.MaxValue), }; }
    }

    #endregion
}

public class XmlSerialiser
{
    public static XmlSerializerNamespaces GetNamespaces()
    {
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add(string.Empty, string.Empty);
        return ns;
    }

    //Creates an object from an XML string.
    public static T FromXml<T>(string xml) where T : new()
    {
        return FromXml<T>(xml, () => new T());
    }

    public static T FromXml<T>(string xml, Func<T> func)
    {
        if (xml == "<nil-classes type=\"array\" />")
        {
            return func();
        }

        XmlSerializer ser = new XmlSerializer(typeof(T));
        StringReader stringReader = new StringReader(xml);
        XmlTextReader xmlReader = new XmlTextReader(stringReader);

        try
        {
            object obj = ser.Deserialize(xmlReader);
            xmlReader.Close();
            stringReader.Close();
            return (T) obj;
        }
        finally
        {
            stringReader.Dispose();
        }
    }

    public static string ToXml(Type type, object obj)
    {
        XmlSerializer ser = new XmlSerializer(type);
        MemoryStream memStream = new MemoryStream();
        XmlTextWriter xmlWriter = new XmlTextWriter(memStream, Encoding.Unicode);

        try
        {
            xmlWriter.Namespaces = true;
            ser.Serialize(xmlWriter, obj, GetNamespaces());
            xmlWriter.Close();
            memStream.Close();
            string xml = Encoding.Unicode.GetString(memStream.GetBuffer());
            xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
            xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
            return xml;
        }
        finally
        {
            memStream.Dispose();
        }
    }

    //Serializes the <i>Obj</i> to an XML string.
    public static string ToXml<T>(object obj)
    {
        return ToXml(typeof(T), obj);
    }
}

public sealed class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id).Column("UserId");
        Map(x => x.Created);
        Map(x => x.Email);
        Map(x => x.Enabled);
        Map(x => x.FirstName);
        Map(x => x.LastActivity);
        Map(x => x.LastLogin);
        Map(x => x.LastName);
        Map(x => x.Password);
        Map(x => x.ScreenName);
        Map(x => x.Config).CustomType(typeof(UserConfig));
    }
}
公共类用户
{
公共虚拟整数Id{get;set;}
公共虚拟字符串电子邮件{get;set;}
公共虚拟字符串屏幕名称{get;set;}
公共虚拟字符串密码{get;set;}
公共虚拟字符串FirstName{get;set;}
公共虚拟字符串LastName{get;set;}
启用公共虚拟bool的{get;set;}
创建的公共虚拟日期时间{get;set;}
公共虚拟日期时间?LastLogin{get;set;}
公共虚拟日期时间?LastActivity{get;set;}
公共虚拟用户配置配置{get;set;}
}
[可序列化]
公共类UserConfig:List,IUserType
{
public UserConfig(){}
公共用户配置(IEnumerable项):基本(项)
{
}
#区域类型成员
公共对象集合(对象缓存,对象所有者)
{
返回DeepCopy(缓存);
}
公共对象深度复制(对象值)
{
返回值;
}
公共对象(对象值)
{
返回DeepCopy(值);
}
公共新布尔等于(对象x、对象y)
{
if(ReferenceEquals(x,y))返回true;
如果(x==null | | y==null)返回false;
返回x等于(y);
}
公共int GetHashCode(对象x)
{
返回x.GetHashCode();
}
公共布尔可换
{
获取{return false;}
}
公共对象NullSafeGet(IDataReader rs,字符串[]名称,对象所有者)
{
object obj=NHibernateUtil.String.NullSafeGet(rs,names[0]);
if(obj==null)
{
返回新的UserConfig();
}
返回XmlSerialiser.FromXml(obj作为字符串);
}
public void NullSafeSet(IDbCommand cmd,对象值,int索引)
{
var参数=(IDataParameter)命令参数[索引];
如果(值==null)
{
parameter.Value=DBNull.Value;
}
其他的
{
parameter.Value=XmlSerialiser.ToXml(值);
}
}
公共对象替换(对象原始、对象目标、对象所有者)
{
归还原件;
}
[XmlIgnore]
公共类型返回类型
{
//此映射到的.Net类型
获取{return typeof(UserConfig);}
}
[XmlIgnore]
公共SqlType[]SqlTypes
{
//此映射到的sql类型
获取{returnnew[]{SqlTypeFactory.GetString(int.MaxValue),};}
}
#端区
}
公共类XmlSerialiser
{
公共静态XmlSerializerNamespaces GetNamespaces()
{
XmlSerializerNamespaces ns=新的XmlSerializerNamespaces();
ns.Add(string.Empty,string.Empty);
返回ns;
}
//从XML字符串创建对象。
公共静态T FromXml(字符串xml),其中T:new()
{
从xml返回(xml,()=>newt());
}
公共静态T FromXml(字符串xml,Func Func)
{
如果(xml==“”)
{
返回func();
}
XmlSerializer ser=新的XmlSerializer(typeof(T));
StringReader StringReader=新的StringReader(xml);
XmlTextReader=新的XmlTextReader(stringReader);
尝试
{
object obj=ser.Deserialize(xmlReader);
xmlReader.Close();
stringReader.Close();
返回(T)obj;
}
最后
{
stringReader.Dispose();
}
}
公共静态字符串ToXml(类型,对象obj)
{
XmlSerializer ser=新的XmlSerializer(t