잊지 않겠습니다.

NHibernate의 Collection Type은 총 4가지를 가지고 있다.
다른 성격을 가지고 있기 때문에 어떤 Collection Type을 사용해야지 될지 고민해야지 된다.

   Bag Set
List
Map
 중복값 허용
 Yes No
Yes
Key must be unique.
Value may be duplicated.
 Ordering  No No
Yes
No
 Type  IList Iesi.Collections.ISet
IList
IDictionary
 Bi-Direction  Yes Yes
No
No

일반적인 Table 구조에서는 Set을 사용하는 것이 EF나 MSLINQ와 동일하게 사용을 할 수 있다.

각각의 hbm.xml은 다음과 같다.

1. Bag


  
    
    
    
      
        
      
      
      
    
  
  
    
      
    
    
    
    
  


2. Set


  
    
    
    
           
         
    
  
  
    
      
    
    
    
    
  


3. List

  
    
    
    
      
        
      
    
  
  
    
      
    
    
    
    
  


4. Map
  
    
    
    
      
       
            
    
  
  
    
      
    
    
    
  


Posted by Y2K
,
NHibernate에서 상속된 model을 지원하는 방법은 총 3가지가 있다.

1. "subclass" 로 하나의 Table에 discriminator column을 지정해서 생성하는 방법
  - discriminator 값으로는 class의 full name이 들어가게 된다.
2. "joined-subclass"로 model의 parent class를 하나의 Table로, 그리고 나머지 model의 property를 묶은 table을 FK로 엮는 방법
3. "union-subclass"로 정규화를 고려하지 않고, 상속된 모든 property를 각각의 table로 구현하는 방법


subclass




  
    
      
    
        
        
    
    
  
  
    
    
    
  
    
  



joined-subclass




  
    
      
    
        
    
    
  
  
    
    
    
  
  
    
        
  


union-subclass




  
    
      
    
        
    
    
  
      
    
    
  
  
        
  


Posted by Y2K
,
NHibernate mapping class의 Id는 natural-Id와 Surrogate Id로 나눌수 있는데, Natural-Id는 기본적으로 사용자가 assign 시켜줘야지 되는 Id가 되고, Surrogate Id는 Instance가 생성되어서 DB에 insert될때, DB를 통해서 채워지는 Id다. NHibernate에서는 Surrogate Id를 사용하는 것을 추천하고 있다. 그럼에도 불구하고, Natural Id를 사용해야지 되는 2가지 경우가 있는데,

1. composite keys에 의해서 Id가 결정이 나는 경우
2. natural key가 실 값이 되는 경우 : 이 경우에는 UserId 같은 특정 값을 사용할 수 있다.

이 두가지 경우를 제외하고, 나머지는 모두다 Surrogate Id를 사용해야지 된다. 다른 경우에는 확실히 코드도 지저분해지고 읽기가 힘들다. (매번 중복되지 않는 값을 확인하고 insert 시켜주는 수고를 덜 수 있다.)


 hilo  Hi/Lo Algorithm에 의하여 integer value가 자동 생성된다.

 guid  System.Guid.NewGuid() method에 의하여 새로운 Guid가 생성된다.

 guid.comb  10byte의 random-guid값이 NHibernate에 의하여 새롭게 생성된다. 이때, 값은 Date와 Time값의 조합으로 만들어진다.

 guid.native  Database에서 얻어지는 Guid값을 이용한다.

 uuid.hex  GUID 값을 32bit hex digital 값으로 dash 없어 얻어낸다.

 uuid.string  GUID깞을 string 형태로 얻어낸다. 사람이 읽을수 없는 값이다.

 counter  계속해서 증가되는 integer값으로 counter값이 생성된다.

 increment  database에서 제공되는 increment 값으로 사용한다.

 sequence  Oracle, DB2, PostreSQL에서 제공되는 sequence의 return값을 이용해서 Id값을 생성한다.

 seqhilo  hilo 알고리즘보다 좀더 좋은 속도를 가진 알고리즘으로 integer value를 자동 생성한다.

 foreign  외래 키 값을 자동으로 카피한다.

 identity  Database에서 생성되는 값을 사용한다.

 sequence-identity  Database에서 제공되는  sequence값을 이용

 trigger-identity  trigger에서 return 시켜주는 값을 이용


마지막으로 native 값을 사용하는 경우, 각 DB의 종류에 따라 각기 다른 값을 사용한다. MSSQL, DB2, MySQL의 경우에는 Integer값을 이용하고, Oracle, Firebird의 경우 sequence와 동일하다.


Posted by Y2K
,

Singletone Pattern

.NET Framework 2009. 3. 31. 03:04
예전에 Design Pattern에서 언급되던 Singletone pattern에서 다른 언어들과 .NET에서 구현상의 큰 차이점이 발견되어서 적어두기. 

1. 다른 언어들.
    public sealed class SingletonOld
    {
        private static Object lockObj = new Object();
        private static SingletonOld singleObject;

        private SingletonOld()
        {
        }

        public static SingletonOld Value
        {
            get 
            {
                if(singleObject == null)
                {
                    lock(lockObj)
                    {
                        singleObject = new SingletonOld();
                    }
                }
                return singleObject;
            }
        }
    }


2. .NET
    public sealed class SIngletonNew
    {
        private SIngletonNew()
        {

        }
        private static SIngletonNew singleObject = new SIngletonNew();

        public static SIngletonNew Value
        {
            get { return singleObject; }
        }
    }

가장 큰 차이는 변수의 선언과 동시에 값의 할당. CLR에서는 변수의 선언과 동시에 값을 할당시켜주는 경우에 생성자의 inline code로 만들어서 넣어주기 때문에 가장 빠르고 static의 경우에는 Single Thread의 접근을 완벽하게 보장해준다. 그러나 예전의 방법대로 구현하게 되면 Thread의 동기화 lock에 의해서 효율성을 떨어뜨리는 구현이 된다. (불행하게도 Design Pattern 책에는 대부분 첫번째 방법으로 되어있다.;; 두번째 방법 비슷하게 private 의 생성자 구현을 만들어두고 이렇게 만들면 안되고 첫번째 방법으로 Thread의 lock을 유지시켜야지 된다는 친절한 설명이 있는 책도 있다.;; )




Posted by Y2K
,
1. FCL과 C# 기본 형식 value type에서 FCL을 사용 
ex : 
 C# 기본 형식 > byte , FCL > System.Byte
-> 조금은 생뚱맞아보이는 방법이긴 하지만, 각 언어들에서 정확한 데이터 형식에 mapping 되는 것을 도와줄 수 있다. 특히 int 값에 대해서 Int32, Int64, Int16 과 같이 다양한 데이터 크기가 있을때, 이에 대한 형식 mapping에서 에러가 발생할 수 있다.
unmanaged code를 사용하는 경우, 이와 같은 일들은 빈번하게 발생 될 수 있기 때문에, 전 code를 FCL로 사용하는 것이 좋다. 

-> 참조 형식과 값 형식에 대해서 명확한 구분이 가능하다. string과 String의 경우에 같은 형식이지만 visual studio에서 색상 설정에 따라서 다르게 보인다. 이는 reference type과 value type을 명확히 구분이 가능하도록 visual studio에서 설정되어있기 때문이다. 그렇지만, C# 기본 형식으로 표기를 할 경우에는 value type인 int와 reference type인 string의 표기가 같게 된다. 

2. Value type과 reference type의 사용에 있어 주의 
-> 기본으로 돌아가서, value type은 stack, reference type은 heap에 생성된다. data의 memory 위치에 따라서 GC의 동작 및 GC 수집의 횟수에 영향을 미치게 된다. 

3. \r\n 을 사용하는 것보다는 Environment.NewLine을 사용하라. 
-> Mono를 비롯하여 CLR의 다른 OS로의 포팅이 계속해서 지원되고 있다. 
: 언제나 다 되려나... 싶긴 하다만.;


FCL 부분의 경우에는 많은 논란이 있을 것 같은 부분이다. 일반적으로 사람들이 사용하는 코드와는 많은 차이를 줄 수 있으니까. 그렇지만, 직접 사용해본 결과로는 보다 더 코드를 알아보기에 편한 느낌이 든다. 무엇보다 Value type과 Reference type간의 눈으로 보이는 차이는, 형식의 선언에서 한번 더 생각해보는 계기를 가지고 오는 것 같다. 
Posted by Y2K
,

CLR via C#

.NET Framework 2009. 3. 24. 01:10
.NET을 사용해서 개발한지 4년정도가 되어가는 때에 딱 적당하게 찾아서 보고 있는 책이라는 느낌이 든다. 
이 책은 기본적으로 .NET을 사용하고 있는 개발자를 대상으로 하고 있는 책으로 대부분의 책에서 나오는 Hello World와 같은 실행되는 코드는 전혀 나오지를 않는다. 

한 언어 또는 Framework를 다루는 책중에서 이정도까지 코드가 나오지 않는 책이 있었는지.. 라는 생각이 무척 많이 들게 하는 책으로 .NET Framework의 밑바닥에서 어떤 일들이 일어나는지.. method로 정의가 될때와 property로 정의될때 JIT의 compile이 어떤 식으로 구현이 되는지와 event의 등록 및 해재가 일어날때, CLR에서 어떻게 처리가 되고 있는지에 대한 그 전에는 단지 '사용하고 있었던' 코드에 대한 밑바닥 지식을 제공한다. 

method를 만들고, property를 만들때 각각 어떤 상황에서 어떤 방법을 사용하면 좋을지에 대한 고민을 한번 심각하게 하게 만들고, 내가 가진 coding 습관중에서 몇가지를 바꿔야지 될 것들이 보인다. 조금은 특이할지 모르겠지만 이 책에서 이야기 하는 논리에 상당히 설득을 당한 상태이기도 하고. 약간... 아니 많은 코딩 습관에 대해서 다시 한번 질문을 던져주는 책이 되는 것 같다. 

그리고, 가장 중요한 것. value와 reference에 대한 고민을 한번 더 나한테 던지고 있다. 어떤 상황에서 어떤 type을 사용하는 것이 가장 좋은가. 라는 고민이 계속해서 생기게 된다. 과연 내가 개발을 할 때, 적정한 상황에서 적정한 type을 이용하고 있는 것일까? 라는 고민과 좀더 나은 방법으로 개발할 수 있는 방법은 무엇일까? 라는 고민들이 계속해서 내 머리속을 스쳐지나가고 있다. 

어떻게 하면 좋은 프로그램을 만들수 있을까? 라는 끝없는 고민을 던져 주면서, .NET Framework의 개발 이론에서 이런 부분이 있어서 이렇게 되었구나.. 하는 깨달음을 주는 책. 정독으로 읽어도 읽어도 계속해서 놓친 점이 나오고 있다. 

지금까지 읽었던 .NET 책중에서 최고라고 칠 수 있고, 내가 다른 사람들에게 추천을 한다면 .NET Gotcha를 읽어본 사람이 읽어볼 책으로 추천하고 싶다. 이 책은 물고기 잡는 법을 가르쳐주지를 않는다. 이 책은 물고기에 대해서 가르쳐주는 책이다. 
Posted by Y2K
,
마지막으로 Thread에 safe 하지 못한 약점을 극복하기 위한 방법은 은근히 간단하다.
.NET 에서 Process -> AppDomain -> Thread -> Context 단위로 동작하기 때문에, Thread에 safe 한 상황은 Process에서 역시 safe 한 상황이 되어야지 된다.

이는 여러개의 Process에서 같은 자원을 사용하는 상황과 거의 동일하기 때문에, Thread에서 사용되는 ManualResetEvent, AutoResetEvent, lock, Monitor 등을 사용하게 되면, 절대로 Thread safe가 될 수가 없다.

따라서, Process단위의 자원 격리가 필요하다. 이를 위해서, Mutex나 Semaphore를 사용하면 간단히 구현 가능하다. ^^
Posted by Y2K
,
먼저, AppDomain을 이용한 개발을 하는 가장 큰 장점은 Process보다 더 가벼운 단위로의 동작이 가능하고, Programming 적으로 동작이 가능하다는 장점을 가지고 있다. 그렇지만, 이러한 동작 및 Resource의 제어를 하기 위해서 모든 AppDomain를 제어하고, Process당 하나만 존재하는 진정한 Singleton object가 필요하게 된다.

이 Singleton object를 만들기 위한 컨셉은 다음과 같다.

Singleton object가 동작할 working AppDomain의 지정


using System;
using System.Runtime.InteropServices;

namespace ProxyServerForMsn.AppDomainManager
{
    public class CrossAppDomainSingleton<T> : MarshalByRefObject where T : new()
    {
        private const string AppDomainName = "Singleton AppDomain";
        // Singleton Working AppDomain 지정
        private static T instance;

        public static void ListupAppDomains()
        {
            IntPtr enumHandle = IntPtr.Zero;
            mscoree.CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
            try
            {
                host.EnumDomains(out enumHandle);

                object domain = null;
                while(true)
                {
                    host.NextDomain(enumHandle, out domain);
                    if(domain == null)
                    {
                        break;
                    }
                    AppDomain appDomain = domain as AppDomain;
                    if(appDomain != null)
                    {
                        Console.WriteLine(appDomain.FriendlyName);   
                    }
                }
            }
            finally
            {
                host.CloseEnum(enumHandle);
                Marshal.ReleaseComObject(host);
                host = null;
            }
        }

        private static AppDomain GetAppDomain(string friendlyName)
        {
            IntPtr enumHandle = IntPtr.Zero;
            mscoree.CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
            try
            {
                host.EnumDomains(out enumHandle);

                object domain = null;
                while(true)
                {
                    host.NextDomain(enumHandle, out domain);
                    if(domain == null)
                    {
                        break;
                    }
                    AppDomain appDomain = (AppDomain)domain;
                    if(appDomain.FriendlyName.Equals(friendlyName))
                    {
                        return appDomain;
                    }
                }
            }
            finally
            {
                host.CloseEnum(enumHandle);
                Marshal.ReleaseComObject(host);
                host = null;
            }
            return null;
        }

        public static T Instance
        {
            get
            {
                if (null == instance)
                {
                    AppDomain appDomain = GetAppDomain(AppDomainName);
                    if (null == appDomain)
                    {
                        appDomain = AppDomain.CreateDomain(AppDomainName);
                    }
                    Type type = typeof (T);
                    T createInstance = (T) appDomain.GetData(type.FullName);
                    if (null == createInstance)
                    {
                        createInstance =
                            (T) appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
                        appDomain.SetData(type.FullName, createInstance);
                    }
                    instance = createInstance;
                }
                return instance;
            }
        }
    }
}


Generic으로 만들어져있기 때문에, 사용될 constraint class의 경우에도 MarsharByObject class를 상속받거나 Serializable 속성을 가져야지 된다.

사용은 다음과 같다.

[Serializable]
public class CrossAppHello
{
public void ConsoleOut()
{
Console.WriteLine("Hello World");
}
}

class Program
{
public static void Main()
{
CrossAppDomainSingleton<CrossAppHello>.Instance.ConsoleOut();
}
}

Posted by Y2K
,
.NET에서의 Application의 구조는 Process->Application Domain->Thread->Context 로 구성이 된다.

일반적으로 개발되는 console app들은 한개의 Process를 가지고, 이는 실행 파일의 이름과 동일한 Default AppDomain을 가지게 된다. 거의 대부분의 프로그램들이 1개의 AppDomain으로 동작하게 되어서, 일반적으로 사용할때는 Process = AppDomain의 형태로 프로그램의 제작이 가능하다.

그렇지만, 1개의 Process에서 여러개의 AppDomain을 가지게 되는 프로그램들이 꼭 필요하게 되는데. 대표적인 프로그램이 IIS라고 할 수 있다. IIS의 동작은 iis_work에 대한 한개의 Process에서 각 Http Application의 접속 Connection에 따라서, 각각 AppDomain을 작성하게 되는데. Application Domain으로 동작하게 되면, 다음과 같은 장점이 있다.

1. Process 단위로 동작하는 것으로 가정하고 프로그래밍이 가능하다.
2. Process로 동작되는 것보다 시스템 리소스 소모가 작다.
3. 관리된 이름으로의 접근이 가능하다.

string appDomainName = string.Format("Server[{0:HH:mm:ss}_{1}]", DateTime.Now, DateTime.Now.Millisecond);
AppDomain appDomain = AppDomain.CreateDomain(appDomainName);
- AppDomain 생성

Console.WriteLine("Start AppDomain : {0}", appDomainName);
ProxySocketHandler proxySocketHandler = (ProxySocketHandler)appDomain.CreateInstanceAndUnwrap(
                                                                 Assembly.GetExecutingAssembly().FullName,
                                                                 "ProxyServerForMsn.SocketHandler.ProxySocketHandler");
- 생성된 AppDomain에서 Instace 생성

AppDomain 단위로 개발을 하게 되면 가장 큰 장점이라고 할 수 있는 것이.. 결국은 리소스의 관리에서 좀 더 원활한 개발을 할 수 있게 된다는 점이다. 그렇지만, AppDomain단위로 개발하는 것이 언제나 좋은 것은 아니다. 개발에서 귀찮은 점이 두가지가 생기게 된다. 하나는 static instance에 대한 사항이고, 나머지 하나는 thread safe에 대한 고려사항이다.

먼저, static instance는 모든 Process당 한개의 고정된 Instance를 보장하지 않는다. 정확히 .NET에서 static intance는 Process 당이 아닌 모든 AppDomain에 해당되는 single instance를 의미한다. 우리가 일반적으로 사용하는 application에서 이와 같은 점이 고려되지 않는 이유는 거의 대부분의 application들이 하나의 AppDomain만을 가지고 있기 때문이다. 이 점이 IIS에서 아무리 class들을 static으로 만들어도, 모든 web page나 connection에서 static instance 또는 method로 사용할 수 없는 이유가 된다.

그리고, thread safe에 대한 고려사항이다. 결론적으로 이야기 한다면, 일반적으로 thread safe를 보장하기 위해서 사용되는 lock 키워드나, ManualResetEvent, AutoResetEvent를 모두 사용하지 못한다. 이러한 방법들은 모두 한개의 AppDomain에서 동작하는 Thread들에서 서로간의 간섭을 알 수 있지, 다른 AppDomain에서의 간섭을 전혀 알지 못한다. Thread에 대한 friendly name은 내부적으로 AppDomainName과 ThreadName의 조합으로 만들어지기 때문에 이와 같은 사항을 전혀 고려할 수 없게 된다.


Posted by Y2K
,
Unit Test에서 사용되는 NUnit를 보면 [Test] attribute와 [TestFixture] attribute를 이용해서 각각의 Test Class와 Method를 모두 얻어내게 된다.

먼저, Assembly의 Load는 간단하게 Assembly의 static method를 통해서 얻어올 수 있다.

Assembly assem = Assembly.LoadFile(PlugInFileName);

여기에서 Assembly의 각 Class의 Attribute의 조건을 얻어오기 위해서 static class를 하나 생성

public static bool HasAttribute(MemberInfo member, string attrName, bool inherit)
{
    object[] attributes = member.GetCustomAttributes(inherit);
    foreach(Attribute attribute in attributes)
    {
        if(IsInstanceOfType(attrName, attribute))
        {
            System.Diagnostics.Debug.WriteLine(attribute);
            return true;
        }
    }
    return false;
}

얻어진 결과를 linq를 이용해서 검색. (이런 곳에서도 linq는 이용 가능하다. ^^)

var types = from t in assem.GetTypes()
            where Reflect.HasAttribute(t, typeName, true) 
            select t;

이렇게 얻어온 Class를 Activator를 이용해서 Instance를 생성해주면 외부 assembly가 가지고 있는 class의 동적 이용 및 NUnit과 비슷한 동작을 하는 것이 가능해진다. 

ISYNCmailPlugIn plugIn = Activator.CreateInstance(type) as ISYNCmailPlugIn;

Remote에서 사용하게 될 경우에 장점은 다음과 같다. 
1. Compile시에 Dll이 필요하지 않다. 
: 동적 load가 되기 때문에 잦은 변경이 있는 lib인 경우에 사용이 편리하다. 

2. 특정한 Interface나 Attribute를 가지고 있는 경우에 외부에서 사용이 가능하다.
Posted by Y2K
,