2011-02-10 3 views
0

모든 메모리가 사용되는 곳에서 시도하고 시도하기위한 입찰에서 NHibernate를 조금이라도 보여주기 시작한 며칠 동안 웹 어플리케이션을 프로파일 링 해왔다. - 메모리 굶주린 짐승하지만 아래로 정말 마지막 일을 해요 :NHibernate IUserType에 대한 이해와 그것이 메모리에 남아있는 이유

사용자 정의 유형이 메모리에 보관하는 것, 메모리의 비트가

누출의 원인

** 아래의 코드를 실행 한 후, I 기억을 윤곽을 그렸다. UserConfig의 인스턴스가 남았습니다. (사용자 인스턴스는 GC로 처리되었습니다.) 사용자가 갔을 때 UserConfig의 인스턴스도 필요하다고 예상했을 것입니다. "인스턴스 보유 그래프"는 다음을 나타냅니다.

UserConfig | NHibernate.Type.CustomType | NHibernate.Type.IType [] | NHbernate.Persister.Entity.SingleTableEntityPersister | System.Collections ..... | System.Collections ..... | NHibernate.Impl.SessionFactoryImpl | SessionSource

여기에 몇 가지 코드를 포함 시켰습니다. 코드 자체에 대해서는 언급하지 마십시오.이 문제를 테스트하기 위해이 코드를 작성 했으므로 프로덕션 준비가되어 있지 않습니다.

메모리 스냅 샷에있는 모든 것이 문제의 일부가되도록 GC.Collect()를 모듈 끝에 추가했습니다.

나는 단지 아니 CustomType를 사용하여 옵션을 선택하지 않습니다 (당신이 전체 프로젝트를 원하는 경우 나에게 이메일을 보내 주시기 바랍니다)

코드의 관련 부분을 포함 시켰습니다. 세션 공장

... 요청 HttpModule을 당

  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); 
     } 

세션

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 = "[email protected]"; 
     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; 
    } 

사용자 클래스 및 사용자지도

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)); 
    } 
} 

답변

2

문제는 당신이 데이터와 조작을 나타 내기 위해 같은 클래스를 사용하고 있다는 점이다. 또한 도메인 모델을 지속성과 결합합니다.

정확하게 이해하면 User.Config은 단일 필드에서 XML로 직렬화하려는 문자열 목록 일뿐입니다.

당신은 List<string> (또는 IList<string>)와 같은 속성을 만들고, XML에 그 직렬화 할 수있는 IUserType을 만들어야합니다

은 '당신은 당신처럼 유창하게 매핑 할 수 있습니다, 그 후

(나는 XMLStringListType를 호출 것) 해왔다.

+1

디에고와 동의합니다. IUserType의 이름이 잘못 지정된 인터페이스 일 수 있습니다. 데이터베이스 필드와 귀하의 poco 사이의 변환 파이프 라인이 더 많습니다. 이것은 POCO가 깨끗하게 유지되기를 원하기 때문에 의미가 있습니다.이 경우 POCO는 NHibernate 자체에 종속됩니다. 좋은 일이 아닙니다. – Rich

+1

@ 리치는 좋은 지적입니다. 나는 IType/IUserType이 처음에는 혼란 스럽다는 것을 발견했다. (이것은 Java에서 온 것임을 기억하자. IUserTypeMapper가 더 나은 이름이었을 것입니다. –

관련 문제