잊지 않겠습니다.

개인적 관심사로 NoSQL중 RDBMS와 가장 유사하기도 하고, 중대용량에 적합하다고 하는 mongodb를 좀 건드려봤다. 
http://mongodb.org 에서 BSON driver를 다운 받고, driver를 이용하면 매우 쉽게 mongodb를 handling할 수 있다.


간단한 test code들은 다음과 같다.

 
[TestFixture]
public class MongoDbTest
{
    [Test]
    public void CreateSchema()
    {
        string connectionString = "mongodb://localhost";
        //서버 접속
        MongoServer server = MongoServer.Create(connectionString);

        //Database 이름 설정
        var dbSetting = server.CreateDatabaseSettings("mongoDbTest2");
        dbSetting.Credentials = new MongoCredentials("ykyoon", "newPassword");
        var db = server.GetDatabase(dbSetting);

        Assert.That(db != null);

        //Create Table
        db.CreateCollection("NewCollection");
        var collection = db.GetCollection("NewCollection");
    }

    [Test]
    public void InsertTest()
    {
        string connectionString = "mongodb://localhost";
        MongoServer server = MongoServer.Create(connectionString);

        var data01 = new BsonDocument();
        data01.Add("author", "ykyoon");
        data01.Add("name", "first bsonDocument");

        //var dbSetting = server.CreateDatabaseSettings("mongoDbTest");
        //var dbNames = server.GetDatabaseNames();

        var db = server.GetDatabase("mongoDbTest");
        //db.CreateCollection("Users");

        var userCollection = db.GetCollection("Users");
        userCollection.Insert(data01);

        Assert.That(server.DatabaseExists("mongoDbTest"));
    }

    [Test]
    public void SelectTest()
    {
        var connectionString = "mongodb://localhost";
        MongoServer server = MongoServer.Create(connectionString);
        var db = server.GetDatabase("mongoDbTest");

        var userCollection = db.GetCollection("Users");
        var qq = Query.EQ("author", "ykyoon");

        var users = userCollection.Find(qq);
        foreach(var user in users)
        {
            Console.WriteLine(user);
        }
    }
}
Posted by Y2K
,
.NET으로 Visual Studio를 이용한 개발환경을 꾸미게 될 때, 

Visual Studio의 Development WebServer는 32bit에서 동작하게 되며, IIS는 64bit에서 동작하기 때문에 2개의 client의 설치가 필요하다.
먼저 기존의 Oracle의 설정을 모두 날리는 작업을 하자. (Oracle의 uninstall은 매우 지저분 하다. -0-)

1. http://www.oracle.com/technetwork/topics/dotnet/downloads/index.html 에서 32bit, 64bit Instant Client를 모두 다운 받는다. (64bit는 Client XCopy version으로.)
2. 32bit -> 64bit 순으로 모두 설치를 해주고, 설치할 때에는 Oracle home name을 각각 다르게 해준다. oracle_x86, orcle_x64 등으로 알아보기 쉬운 이름이 좋다.
3. TNS_ADMIN 환경 설정 변수를 설정해주고,  tnsnames.ora 파일을 만들어서 TNS 설정을 넣어준다. 
4. 개발시에는 32bit를 이용하고, 배포할 때는 64bit를 이용해야지 된다. (local copy option을 반드시 설정해줘야지 된다.)



 
Posted by Y2K
,
QueryOver<> method에서 LINQ 쿼리인 .Contains가 먹히지 않는다.;
이때 다음과 같은 코드를 사용하면 LIKE 검색을 가능하게 할 수 있다.

var restriction = Restrictions.On(m => m.Name).IsLike("%바람과%");
var movies = session.QueryOver().Where(restriction).List();
foreach(var movie in movies)
{
    Console.WriteLine(movie.Name);
}

Posted by Y2K
,
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
,
Oracle에서 REFCURSOR 타입을 사용하는 StoreProcedure를 OUT parameter로 사용하는 경우에는 2가지 제약 사항이 있다.

1. Table Cursor를 사용하는 것이 아닌, SYS_REFCURSOR 를 사용해야지 된다.
2. 1개의 Cursor만 return되는 것을 지원하며, in parameter의 순서가 중요하다. 반드시 SYS_REFCURSOR를 처음에 return시켜줘야지 된다.

이와 같은 규칙을 따르면 다음과 같은 StoreProcedure를 사용하는 경우 다음과 같이 HBM이 구성 가능하다.


CREATE OR REPLACE PROCEDURE FWKUSER.sp_XMMenu_MenuName_Test_q
  (
   spcur        OUT sys_refcursor, 
   p_menu_name    IN VARCHAR2) IS
BEGIN
  OPEN spcur FOR
    select   
    *         
   
    from   xm_menu 
    where   menu_name like '%' || p_menu_name || '%'
    and        fold_yn = 'N'
    and        hidden_yn = 'N'
    and        use_yn = 'Y'
    and        del_yn = 'N'
    order by instr(menu_name, p_menu_name, 1, 1), menu_name;
END;
/


  
    
            
      
      
            
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
        
  


이렇게 구성된 hbm을 이용해서 다음과 같이 StoreProcedure를 호출 할 수 있다.

using(var session = sessionFactory.OpenSession())
{
    var query = session.GetNamedQuery("GetMenus");
    query.SetParameter("menuName", "%");
    var output = query.List();

    foreach(var p in output)
    {
        Console.WriteLine(p.MenuName);
    }
}
Posted by Y2K
,
Network Copy등을 하기 위해서 ID와 Password를 알고 있는데, code 상으로 구현하는 방법을 한번 정리.

일단, WindowsIdentify는 win32 dll을 이용하고, Windows Token을 사용한다. Windows Token은 어떤 시스템에 Logon한 이후에, 일정시간 유지가 되는 Windows의 memory안에 자신의 Identity를 저장하게 된다. 

먼저, Win32의 [DllImport("advapi32.dll", SetLastError = true)] 를 이용해서 LogonUser method를 dll import 시켜온다.

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);


마지막 값인 phToken값을 이용해서 WindowsIdentify를 얻어 내면 타 시스템에 접근이 가능하다. 여기에서 userName은 upn형태로 넣어져야지 되며, 이 형식은 ykyoon@vplex.net 과 같은 email과 같은 형식으로 입력이 되어야지 된다.

다음은 NetworkCopy를 하는 Test code이다. 




[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr* Arguments);

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

public unsafe static string GetErrorMessage(int errorCode)
{
    int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
    int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
    int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

    int messageSize = 255;
    String lpMsgBuf = "";
    int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

    IntPtr ptrlpSource = IntPtr.Zero;
    IntPtr prtArguments = IntPtr.Zero;

    int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments);
    if(0 == retVal)
    {
        throw new Exception("Failed to format message for error code " + errorCode + ". ");
    }

    return lpMsgBuf;
}

static void Main(string[] args)
{
    const int LOGON32_PROVIDER_DEFAULT = 0;
    const int LOGON32_LOGON_INTERACTIVE = 2;
    const int SecurityImpersonation = 2;

    var tokenHandle = IntPtr.Zero;
    bool returnValue = LogonUser(@"Administrator@vplex.net", "network ip in here", "this is password", 9, 0, ref tokenHandle);
    Console.WriteLine("LogonUser called.");
    if(false == returnValue)
    {
        int ret = Marshal.GetLastWin32Error();
        Console.WriteLine("LogonUser failed with error code : {0}", ret);
        Console.WriteLine("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret));
        int errorCode = 0x5; //ERROR_ACCESS_DENIED
        throw new System.ComponentModel.Win32Exception(errorCode);
    }
    else
    {
        var ykyoonAdmin = new WindowsIdentity(tokenHandle);
        var wic = ykyoonAdmin.Impersonate();
        File.Copy("C:\\System.Web.Mvc.dll", "\\\\10.30.3.24\\c$\\abc.dll", true);
        wic.Undo();
        wic.Dispose();
    }
    CloseHandle(tokenHandle);
}
Posted by Y2K
,
PropertyInfo를 이용해서 간단히 슥삭. 너무나 편하게 debuging할 수 있는 방법이라서 자주 사용하기도 하고, 긁어서 그냥 넣어주면 편해서 올려둔다. 기본적으로 Property Invoke를 이용해서 각 값을 얻어내고, 그 값을 표시하는 방법인데... 이모저모로 편하구. 



public override string ToString()
{
    StringBuilder sb = new StringBuilder();
    sb.AppendLine(string.Format("<{0}>", GetType().Name));
    foreach(PropertyInfo property in GetType().GetProperties())
    {
        string propertyName = property.Name;
        object value = property.GetValue(this, null);
        if(value != null)
        {
            if(value is IEnumerable && !(value is string))
            {
                sb.AppendLine(string.Format("<{0}>", propertyName));
                IEnumerator enumerator = ((IEnumerable)value).GetEnumerator();
                while(enumerator.MoveNext())
                {
                    sb.AppendLine(enumerator.Current.ToString());
                }
                sb.AppendLine(string.Format("", propertyName));
            }
            else
            {
                sb.AppendLine(string.Format("<{0}>{1}", propertyName, value.ToString()));
            }
        }
    }
    sb.Append(string.Format("", GetType().Name));
    return sb.ToString();
}
Posted by Y2K
,
회사에서 신규 프로젝트에서 Spring.NET을 사용하기로 해서, 간단히 자료 찾아보고 공부. 끄적끄적.
개인적으로는 .NET MVC에는 Castle이 가장 어울리는 것이 아닌가 싶은데, 일단 제일 유명하고, 잘 만들어져있다는 Spring의 .NET 포팅버젼을 공부해놓는 것도 도움이 되겠다는 생각으로 시작하게 되었다.

먼저, WebService, Console Application, Web등 Context 소비자측에서 사용될 Interface를 선언한다. Interface를 통해서 구성이 되고, IoC로 구성이 될 경우 Interface의 변경은 Context 소비자의 변경을 가지고 올 수 있기 때문에 Interface의 변경에는 매우 주의를 요하게 된다.
    public interface IVirtualMachine
    {
        string CreateVM(string description, string hwProfileTextKey, string vmiName, string zoneTextKey,
            string accountName, string sequrityGroupName);
        string GetVMConfiguration(string vmName);
        string StartVM(string vmName);
        string ShutdownVM(string vmName);
        string RestartVM(string vmName);
        string StopVM(string vmName);
        string DeleteVM(string vmName);
        string SetMemorySize(string vmName, int memorySize);
        string migrateVM(string vmName, string toHostName);
        string GetVMCurrentUsage(string vmName);
        string GetPasswordData(string vmName);
    }

그리고, Interface를 상속받는 Context를 구성한다.
    public class VirtualMachine : IVirtualMachine
    {
        #region IVirtualMachine Members

        public string CreateVM(string description, string hwProfileTextKey, string vmiName, string zoneTextKey, string accountName, string sequrityGroupName)
        {
            string message = string.Format("CreateVM : {0}", description);
            return message;
        }

        public string GetVMConfiguration(string vmName)
        {
            string message = string.Format("GetVMConfiguration from Common Service : {0}", vmName);
            return message;
        }

        public string StartVM(string vmName)
        {
            throw new NotImplementedException();
        }

        public string StopVM(string vmName)
        {
            throw new NotImplementedException();
        }

        public string ShutdownVM(string vmName)
        {
            throw new NotImplementedException();
        }

        public string RestartVM(string vmName)
        {
            throw new NotImplementedException();
        }

        public string DeleteVM(string vmName)
        {
            throw new NotImplementedException();
        }

        public string SetMemorySize(string vmName, int memorySize)
        {
            throw new NotImplementedException();
        }

        public string migrateVM(string vmName, string toHostName)
        {
            throw new NotImplementedException();
        }

        public string GetVMCurrentUsage(string vmName)
        {
            throw new NotImplementedException();
        }

        public string GetPasswordData(string vmName)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

Config 파일에 각 Context의 선언을 넣는다.

그리고, 사용할 소비자에서는 Spring.Context.Support.ContextRegistry 를 통해서 각 Context를 얻고, Object들을 Interface를 통해서 얻어 사용한다. 이때, 객체를 얻어와서 사용해도 되지만, 객체를 얻어오게 되면 Context의 변경이 있을때, 일관성이 떨어지고, 강한 결합으로 연결되기 때문에 n-Tier 구성에 어울리지 않게 된다. 반드시 인터페이스를 통해서 사용을 해야지 된다.

그리고, Context가 변경되게 되는 경우에는 다른 이름으로 Context를 추가하고, 정해진 Context Name을 이용해서 사용해주면 된다. 그리고, 생성자에 입력값이 있는 경우 다음과 같이 사용하면 된다.



가장 좋은 방법으로 생각되는 것은 appSettings에 사용될 Context Object Name을 추가하면 Context의 Re-Build없이 Context Object를 변경해서 사용하는 것이 가능하기 때문에 이 방법이 가장 좋을 듯 하다.



다음은 Context 소비자 코드이다.
static void Main(string[] args)
{
    string contextName = System.Configuration.ConfigurationManager.AppSettings["VirtualMachine"];
    
    IApplicationContext context = ContextRegistry.GetContext();
    IVirtualMachine vmService = (IVirtualMachine) context.GetObject(contextName);

    string result = vmService.GetVMConfiguration("YKYOON");
    Console.WriteLine(result);
}





Posted by Y2K
,