2012년 3월 14일 수요일

NuGet을 사용합시다.

그동안 프로젝트 구성 시 사용하는 라이브러리  관리에 애를 먹으셨죠? NuGet 을 사용하시면, 허무할 정도로 쉽게 라이브러리 참조를 구성할 수 있습니다.

그동안 각 프로젝트마다 참조를 구성하기 위해, 또 Update 될 때마다 수동으로 Library를 복사하고, Runtime Assembly Binding 을 설정해줘야 하고…

가장 힘든 부분이지만 그 동안은 매뉴얼을 보고, 설정도 개인이 수행해야 했지만, NuGet을 사용하면, 자동으로 설정을 추가해주는 방식이라 초보자에게는 상당히 도움이 될 수 있습니다. (물론 양날의 칼이긴 합니다)

또 한가지 요즘에 ASP.NET UI 쪽 프로젝트를 다루는 데, 많은 javascript 를 사용해야 하더군요. 예전에는 그냥 복사해서 썼는데, JavaScript Framework으로 발전하여, 서로간의 version 매칭이 되어야 하고, 각기 다른 위치에 저장되어야 할 내용이 있기도 합니다. ( css, images 등 ) 

이런 JavaScript Framework 관련 추가는 NuGet을 이용하는 것이 훨씬 유리하겠지요?

다음으로 제가 담당하는  Solution 을 보시면 이해가 훨씬 빠를 것입니다.

NuGet은 솔루션 단위로 라이브러리를 관리합니다. 이에 한 솔루션에 많은 프로젝트가 있다면, 반복작업을 한번만 수행해도 되므로, 효율적인 측면에서는 상당히 유리하겠지요?

우선 VS.NET Tools -> Library Package Manager –> Manage NuGet Package for Solution 를 선택하여 아래와 같이 원하는 라이브러리를 설치합니다.

nuget_package_manager

설치가 끝나면, 어떤 프로젝트에 실제로 참조를 설정할 것인지 선택하는 화면이 나옵니다. 보시다시피, 이 솔루션에는 85개 정도의 프로젝트가 있고, 특히 .NET 4.0 프로젝트와 Silverlight 4.0 프로젝트가 혼용되어 있습니다. 이렇게 혼용되어 있다 더라도, 설치할 Package 가 각 프로젝트의 환경을 지원한다면, 각자 알맞는 라이브러리 및 환경을 설정해 준다는 것입니다.

select_projects

85개의 프로젝트를 한번에 구성한 것이 아니기에, 손으로 구성했지, 아마 한꺼번에 85개를 손으로 구성하라고 했다면 ~ 커헉…. 이 얼마나 판타스틱한 방식입니까?

더군다나 NuGet 공식 서버가 죽었을 경우를 대비해, Private NuGet Feed 를 구성할 수도 있고, 내부 Package를 내부 Feed에서만 제공할 수도 있습니다. ( 참고 : Hosting Your Own NuGet Feeds )

자신이 만든 라이브러리를 공식 NuGet Feeds 에 올려 배포할 수 도 있고, 회사 내부에서만 배포할 수 도 있으니, 실제 배포본 만드는 방법은 Using A GUI ( Package Explorer ) to build package 을 참고하시면 되겠습니다.

버전별 충동이라던가, Dependency라던가 하는 부분도 회사 중앙에서 관리가 가능하므로, 버전문제로 되네 안되네 하는 문제가 줄어들 것입니다.

2012년 3월 12일 월요일

NHibernate ICriterion 의 장점

NHibernate 에서도 LINQ 를 지원하고, 상당한 범위의 질의 방법을 제공합니다.
다만, 기존 ICriterion 을 이용할 수 밖에 없는 상황이 아직도 많습니다. 특히 HQL을 사용하는 것도 난감한 상황이 많습니다.

예전에는 HQL과 Criteria 질의 방식에 대한 비교에 대해 글을 썼었는데, 이제는 LINQ 방식까지 도입해서 비교해야 보았습니다.

결론부터 말씀드리면, “Lambda Expression을 이용하여 ICriteria 를 빌드해주는 QueryOver가 가장 종은 해법이다.” 라고 말씀드릴 수 있겠습니다. (제가 LINQ로 복잡한 쿼리를 작성하는 방식에 공부를 하지 않는 것도 있습니다)

다음 코드는 기간 정보 (시작~완료 시각) 이 있는 엔티티에 대해, 검색을 수행하고자 할 때 다음과 같은 방식을 수행해야 합니다.

1. Entity 의 시작시각, 완료시각
2. 검색조건의 시작시각, 완료시각 

이 4개의 조건 값이 NULL 을 가질 수 있고, 특히 검색 조건에서 기간이 없을 때는 모든 기간, 시작시각만 있는 경우는 시작시각 이후 모두, 완료시각만 있는 경우는 완료시작까지… 뭐 이런 조건을 만들어 낼 수 있습니다.

다음 코드는 대상 기간과 검색 기간의 겹치는 기간이 있는지를 알 수 있는 Criterion을 빌드합니다.

/// <summary>
///
주어진 기간이 오버랩되는지를 파악하는 Criterion
/// </summary>
public static ICriterion IsOverlapCriterion(this ITimePeriod period, string loPropertyName, string hiPropertyName)
{
period.
ShouldNotBeNull("range");
Guard.Assert(period.IsAnytime == false, @"기간이 설정되어 있지 않습니다. 상하한 값 모두 없으므로, 질의어를 만들 필요가 없습니다.");
loPropertyName.
ShouldNotBeWhiteSpace("loProperty");
hiPropertyName.
ShouldNotBeWhiteSpace("hiProperty");

if(IsDebugEnabled)
log.Debug("Build IsOverlapCriterion... range={0}, loPropertyName={1}, hiPropertyName={2}",
period, loPropertyName, hiPropertyName);

if(period.HasStart && period.HasEnd)
{
return Restrictions.Disjunction()
.
Add(period.Start.IsInRangeCriterion(loPropertyName, hiPropertyName))
.
Add(period.End.IsInRangeCriterion(loPropertyName, hiPropertyName))
.
Add(loPropertyName.IsBetweenCriterion(period.Start, period.End))
.
Add(hiPropertyName.IsBetweenCriterion(period.Start, period.End));
}

if(period.HasStart)
{
return Restrictions.Disjunction()
.
Add(period.Start.IsInRangeCriterion(loPropertyName, hiPropertyName))
.
Add(Restrictions.Ge(loPropertyName, period.Start))
.
Add(Restrictions.Ge(hiPropertyName, period.Start));
}

if(period.HasEnd)
{
return Restrictions.Disjunction()
.
Add(period.End.IsInRangeCriterion(loPropertyName, hiPropertyName))
.
Add(Restrictions.Le(loPropertyName, period.End))
.
Add(Restrictions.Le(hiPropertyName, period.End));
}

throw new InvalidOperationException("기간이 Overlap되는지 판단하는 Criterion을 생성하기 위한 조건이 맞지 않습니다.");
}


다음으로는 특정 엔티티의 값에 대해 BETWEEN 조건을 주는 방식입니다. 검색 값의 NULL 여부에 따라 달라집니다.



/// <summary>
///
Between (상하한을 포함하는 구간의 값을 구한다. 상하한에 대한 구간 검증은 하지 않는다!!!)
/// </summary>
/// <param name="propertyName">
속성명</param>
/// <param name="lo">
하한</param>
/// <param name="hi">
상한</param>
/// <returns></returns>
public static ICriterion IsBetweenCriterion(this string propertyName, object lo, object hi)
{
propertyName.
ShouldNotBeWhiteSpace("propertyName");

if(lo == null && hi == null)
throw new InvalidOperationException("Between 을 사용할 상하한 값 모두 null이면 안됩니다.");

if(IsDebugEnabled)
log.Debug("Between Criteria를 빌드합니다... propertyName=[{0}], lo=[{1}], hi=[{2}]", propertyName, lo, hi);

if(lo != null && hi != null)
return Restrictions.Between(propertyName, lo, hi);

// lo, hi 값 중 하나가 없다면
var result = Restrictions.Conjunction();

if(lo != null)
result.
Add(Restrictions.Ge(propertyName, lo));

if(hi != null)
result.
Add(Restrictions.Le(propertyName, hi));

return result;
}



다음은 BETWEEN 과는 반대로 특정 값이 엔티티의 두 개의 속성값의 범위 내에 있는지 파악하는 질의어를 빌드하는 메소드입니다.


/// <summary>
///
지정한 값이 두 속성의 값 범위 안에 있을 때 ( Between 의 반대 개념 )
/// </summary>
/// <param name="value"></param>
/// <param name="loPropertyName"></param>
/// <param name="hiPropertyName"></param>
/// <returns></returns>
public static ICriterion IsInRangeCriterion(this object value, string loPropertyName, string hiPropertyName)
{
value.
ShouldNotBeNull("value");

if(IsDebugEnabled)
log.Debug("지정한 값이 두 속성의 값 범위 안에 있을 검색 조건을 빌드합니다. " +
@"value={0}, loPropertyName={1}, hiPropertyName={2}", value, loPropertyName, hiPropertyName);

return Restrictions.Conjunction()
.
Add(Restrictions.Disjunction()
.
Add(Restrictions.IsNull(loPropertyName))
.
Add(Restrictions.Le(loPropertyName, value)))
.
Add(Restrictions.Disjunction()
.
Add(Restrictions.IsNull(hiPropertyName))
.
Add(Restrictions.Ge(hiPropertyName, value)));
}




자 보시다시피, 질의를 위한 값의 NULL 여부에 땨라 상당히 복잡한 쿼리를 만들어 낼 수 있습니다. 이 걸 일반 쿼리문 빌더나 HQL 로는 절대로 못할 겁니다. LINQ로는요? 흠.. 글쎄요… IQueryable<TEntity> 이므로 불가능하지는 않겠지만…



이걸 QueryOver 를 이용하도록 변경한다면, Magic String 도 제거가 가능합니다.



/// <summary>

///
주어진 기간이 오버랩되는지를 파악하는 질의어를 빌드합니다. (모든 구간은 폐쇄구간일 필요는 없고, 개방 구간이라도 상관없습니다.

/// </summary>

/// <typeparam name="T">
엔티티 수형</typeparam>

/// <param name="period">
검사할 시간 구간</param>

/// <param name="loExpr">
하한값을 나타내는 속성</param>

/// <param name="hiExpr">
상한값을 나타내는 속성</param>

/// <returns></returns>


public static ICriterion IsOverlapCriterion<T>(this ITimePeriod period, Expression<Func<T, object>> loExpr, Expression<Func<T, object>> hiExpr)

{


    period.
ShouldNotBeNull("period");

   
Guard.Assert(period.IsAnytime == false, @"기간이 설정되어 있지 않습니다. 상하한 값 모두 없으므로, 질의어를 만들 필요가 없습니다.");




   
var loPropertyName = RetrievePropertyName(loExpr);

   
var hiPropertyName = RetrievePropertyName(hiExpr);




   
return CriteriaTool.IsOverlapCriterion(period, loPropertyName, hiPropertyName);

}





최종 사용자가 사용할 구간 Overlap 검색 질의어 빌드 메소드입니다.



public static QueryOver<TRoot, TSub> AddIsOverlap<TRoot, TSub>(
this QueryOver<TRoot, TSub> queryOver,
ITimePeriod period,
Expression<Func<TSub, object>> loExpr,
Expression<Func<TSub, object>> hiExpr)
{
queryOver.
ShouldNotBeNull("queryOver");

queryOver.
UnderlyingCriteria.Add(IsOverlapCriterion(period, loExpr, hiExpr));
return queryOver;
}


뭐 별 거 아닌 거 같지만, HQL이나 LINQ 로는 유연하면서 복잡한 질의어 생성이 불가능하거나 힘들 때는 QueryOver 를 추천합니다.



다만 성능을 생각하는 루틴한 질의는 HQL로, 쉽고 빠른 구현을 원한다면 LINQ 로 하시기 바랍니다.

위의 QueryOver 관련은 Domain Layer 개발자만 알면 되고, 일반 사용자에게는 API 로 제공되는 부분이므로 굳이 모든 사람이 공부할 필요는 없지요.

2012년 3월 3일 토요일

ReSharper 7 EAP for VS.NET 11 Beta

Visual Studio .NET v11 Beta가 출시되었습니다. 당장 깔았지요. Developer Preview와는 UI가 확 달라졌고… 칼라 TV 보다가 흑백 TV 보는 것 같아 처음에는 적응이 안되었습니다.

지금은 좀 적응이 되었지만… 아직도 알록달록 색상이 없으니, 항목들이 구분이 잘 안가는… 좀 그렇네요…

어찌되었건… 기존 Resharper 6.1.1 for VS.NET 11 Developer Preview  가 있어서 그냥 사용할 수 있을 줄 알았는데, 아니더군요. 이틀만에 JetBrains .NET Tools Blog 에 Reshrper 7 EAP 소식이 떴습니다.

원문 : ReSharper 7 EAP for Visual Studio 11 Beta is now OPEN!

첫번째 EAP 라서 버그가 상당히 많을 줄 알았는데, 속도가 좀 느려졌다는 것과 비동기 프로젝트 로드에서 약간의 버벅거림이 있다는 정도입니다…

역시 ReSharper 가 있어야 코딩할 맛이 납니다^^