Stored Procedure return value in Entity Framework

Posted in .NET Framework // Posted at 2013. 5. 9. 18:50

Entity Framework 는 다 좋은데, 저장 프로시저의 return 값을 처리할 수 없다는게 단점이다.

 

물론 시각에 따라서 이것이 단점이냐 하는 것은 논란(?)의 소지가 있겠지만...

실제로, Entity Framework가 SQL을 대체하는 것이 아니라 ORM 프레임워크이기 때문에 SQL의 모든 기능을 지원하지 않는 것이 단점이라고 단정할 수 없다는 예기가... 인터넷 상에 존재한다.

 

하지만 개인적으로 참으로 아쉬운 부분이다.

이미 만들어진 저장 프로시저 들이 return value를 많이 사용하고 있는 상황에서,

모델 계층을 Entity Framework 로 마이그레이션 하려고 하니 더욱 아쉬운 생각이 든다.

 

특히 MS의 또 다른 기술인 LINQ TO SQL에서는 return value를 지원하기에, Entity Framework의 버전 업에 해당 기능이 추가 되기를 기대해 보지만 아직인 듯 하다.

 

그렇다고 Entity Framework를 도입하려고 기존에 잘 운영되고 있는 수 많은 저장 프로시저들을 select 혹은 ouput 변수로 수정한다는 것도 현실적이지 않다.

 

그렇다면 return value 처리를 위해서만 별도로 LINQ TO SQL 혹은 ADO.NET을 사용할 수도 있겠지만,

MODEL 계층의 기술 기반이 복잡해지고 이중화 된다는게 맘에 들지 않는다.

 

그럼. 마지막으로 사용할 수 있는 카드는,

어떤 식으로든 Entity Framework 상에서 저장 프로시저의 return value를 처리하기만 하면 된다.

 

다시 말하지만 '어떤 식으로든...' .

 

다음의 코드는 Entity Framework로 저장 프로시저의 return value를 반환받는 코드이다.

썩 맘에 들진 않지만, 불가피한 경우에 고민해 볼 만 하다.

 

-- 저장 프로시저

CREATE PROC [dbo].[USP_Test]
AS
BEGIN  
 return 5
END

 

//Entity Framework로 저장프로시저의 return value

var returnValueParam = new SqlParameter();
returnValueParam.ParameterName = "@returnValueParam";
returnValueParam.SqlDbType = SqlDbType.Int;
returnValueParam.Direction = ParameterDirection.Output;
var data = context.Database.SqlQuery<int>("EXEC @returnValueParam = [dbo].[USP_Test]", returnValueParam);

               

try
{
        data.SingleOrDefault();                   
}
catch { }

 

string returnValue = returnValueParam.Value.ToString();

 

 

 

Generic DataContract in WCF

Posted in .NET Framework // Posted at 2013. 5. 9. 11:53

WCF 서비스의 반환 값으로 사용자 정의 객체를 사용한다.

WCF 서비스와 클라이언트의 데이터 계약을 위한 DataContract Attribute 설정을 통해 시리얼라이즈 되어 통신이 가능해 진다.

 

그런데 이 반환 객체의 멤버 중, Collection 타입의 멤버는 내부 Item 타입만 다를 뿐이라서 상위 타입 선언을 통해 하나의 클래스만 유지하고 싶었다.

 

예를 들어서 이런 식이다.

[DataContract]

public class ReturnCollectionCategory : ReturnBaseObj
{
       [DataMember]
       public IList<Category> ResultCollection { get; set; }

}

 

[DataContract]

public class ReturnCollectionProduct : ReturnBaseObj
{
       [DataMember]
       public IList<Product> ResultCollection { get; set; }

}

 

위의 코드를 보면, ResultCollection 프로퍼티의 아이템 타입만 다를 뿐 나머지는 모두 동일하다.

따라서 최상위 타입인 System.Object로 아래와 같이 처리하려 했다

 

[DataContract]

public class ReturnCollectionCategory : ReturnBaseObj
{
[DataMember]
public IList<System.Object> ResultCollection { get; set; }

}

 

이렇게 하면 하나의 클래스를 유지하면서 다형적으로 처리할 수 있게 된다.

그런데 문제는 WCF 환경에서 이 객체를 원격 통신용 객체로 이용할 때 발생한다. System.Object 타입을 시리얼라이즈 할 수 없다는 것이다.

 

그래서 아래와 같이 지네릭을 이용하기로 한다.

지네릭을 이용하면, 객체 생성 시점에 타입이 결정되기 때문에 원격 통신에도 문제없이 잘 동작한다.

[DataMember] 
public class ReturnCollectionObj<T> : ReturnBaseObj
{
     [DataMember]
     public IList<T> ResultCollection { get; set; }

 

추가로, 이렇게  서비스 단에서 지네릭 기반으로 생성된 객체를 원격으로 전달받는 클라이언트에서는 지네릭 버전이 아니라 이미 결정된 객체 이름으로 전달받게 된다.

 

즉 서비스 참조로 생성된 프록시 객체를 보면 지네릭 버전의 객체 이름에 임의의 문자열이 추가된 것을 확인할 수 있다. 실제 서비스 환경에서는 이러한 상황이 달갑지 않다.

따라서 아래와 같이 이름을 지정하면 좋을 것이다.

 

[DataContract(Name="ReturnCollectionObj{0}")] 

public class ReturnCollectionObj<T> : ReturnBaseObj
{
[DataMember]
public IList<T> ResultCollection { get; set; }
}

 

이렇게 하면 클라이언트에서 반환 받는 객체 이름은 타입이 결정된 이름이 붙여 진다.

예를 들어 Product 타입으로 객체를 생성했다면, ReturnCollectionObjProduct 가 된다.

 

 

 

 

WCF Data Service VS ASP.NET Web API

Posted in .NET Framework // Posted at 2013. 1. 8. 14:39

기존 프로젝트에 데이터 제공을 목적으로 하는 중계 서비스가 존재하고 있다

 

수 년전에 개발된 거라,

개발 모델이 과거에 머물러 있고 이기종간 다중값의 데이터 교환을 위해 2차원 배열에 의존하고 있는 구조이다.

 

이 서비스를 좀 더 진보된 형태로 개선하기 위한 프로젝트를 착수하게 되었다

 

큰 틀에서의 개선 목적은 다음과 같다

- 더 효율적인 상호 운영성

- 보다 진보된 개발 모델 차용

 

그리고 좀 더 세부적인 것까지 본다면 아래 요건도 고려되어야 한다

- 제공되는 데이터의 가공 용이성 

- 기존 서비스의 마이그레이션 용이성

- 더 좋은 생산성과 유지보수성

- 비즈니스 기능 외 인증,보안 필터와 같은 추가 로직의 삽입 용이성

 

닷넷 개발환경의 관점에서 대략 아래와 같은 개발 모델로 압축 시킬 수 있다

1) WCF 웹 프로그래밍 모델

2) WCF Data Service

3) ASP.NET Web API

 

이 세가지 중, oData 질의를 지원하는 것은 2),3)번이다.

또한 WCF Web API는 ASP.NET Web API로 대체되었다는 MS의 취지와 보다, 진보된 개발 모델 차용이라는 목적에 충실할 경우에도 2),3)번으로 압축할 수 있다

 

그렇다면 과정 WCF Data Service로 할 건인가? ASP.NET Web API로 할 것인가에 귀착된다.

두 개발 모델의 차이점과 선택 기준을 살펴 보기 위해 관련 자료를 검색해 봤다

 

먼저 oData 개념의 근원지이자 닷넷 환경의 개발사인 MS의 기술 자료부터 살펴 보고,

 

http://msdn.microsoft.com/en-us/data/odata

 

이 사이트의 포럼에 검색을 수행해 본다.

http://social.msdn.microsoft.com/Search/en-US/data?query=web%20api&rq=meta:Search.MSForums.GroupID(787c8d54-d241-48d2-8522-bcc5d7e41315)+site:microsoft.com&rn=All+Data+Platform+Development+Forums

 

http://www.codeproject.com/Articles/341414/WCF-or-ASP-NET-Web-APIs-My-two-cents-on-the-subjec

 

그리고 WCF 자체와 비교한 아래 글도 참고할 만 하다

http://mattmilner.com/Milner/Blog/post/2012/02/28/WebAPI-or-WCF.aspx

 

ASP.NET Web API에 대한 스콧 형님의 간단한 단상도 볼 만 하다

http://weblogs.asp.net/scottgu/archive/2012/02/23/asp-net-web-api-part-1.aspx

 

구글의 아래 키워드로 검색하면 적당한 참고 자료를 찾을 수 있다

검색 키워드: wcf data service vs web api

 

기술적인 정보와 참고 자료로 선정하는데 적잖은 혼란을 느끼던 중,

간과해서는 안될 매우 중요한 팩터인 '실제 구축하는 시스템의 성격'이라는 축을 고민하기에 이르렀다

 

실제 구축하는 시스템의 성격은 대략 아래와 같다

 

1) 조회 90%, 입력/수정: 10%

전체 서비스에서 대략 10%를 제외하면 모두 데이터 제공 즉 조회 기능이다.

 

2) 한정된 데이터 제공

선택의 기준에서 아주 중요한 부분이란 생각이 들었다.

현재 개선을 하고자 하는 시스템의 경우 보안 목적으로 중계 서비스 역할을 하고 있다

즉 데이터 제공이 목적이긴 하지만 본질은 클라이언트가 직접 데이터 저장소에 액세스 할 수 없다는 목적을 달성하기 위해 만들어진 중계 서비스인 것이다. 그러기에 데이터를 광범위하게 제공하기 보다는 한정된 데이터를 특정 로직에 의해 가공해서 제공하는 성격이 짙다는 것이다.

 

WCF Data Service의 경우 oData 질의를 필수로 동반하기에, 데이터 제공의 한정을 위해서는 데이터 모델 차원에서 규칙을 부여할 수 밖에 없다. 반면 Web API의 경우 oData 질의는 선택사항일 수 있고 상호 협의된 형태의 데이터 반환만 가능하도록 비즈니스 처리가 가능하다.

 

WCF Data Serivce가 Web API에 비해 보다 더 깊은 oData를 지원한다고는 하지만, 실제 시스템의 활용도 측면에서는 어쩌면 불필요한 요소일지도 모른다.

 

WCF Data Service를 놓지기 싫었던 이유 중 하나는,

클라이언트가 닷넷 기반일 경우 데이터 질의를 LINQ 쿼리로 직접 할 수 있다는 부가적인 장점이었는데 이 부분은 포기해도 무방한 사소한 장점일 수 있다.

 

그리고 개발 생산성 측면에서도 WCF Data Service가 더 적은 코드를 요구하지만 데이터 원본 자체의 넓고 다양한 제공일 경우가 아니고서는(데이터 원본 대비 한정되고 가공된 데이터 제공) 그 가치가 크지 않겠다는 판단이다.

 

MS가 두 가지 유사한 개발 모델을 제공해서 약간의 혼동이 있으나, 결국 중요한 것은 두 개발 모델의 설계 사상과 특징에 기반한 현재 시스템 성격을 투영해 선택하는 것이 최선이 아닐까 한다.

 

'.NET Framework' 카테고리의 다른 글

Stored Procedure return value in Entity Framework  (0) 2013.05.09
Generic DataContract in WCF  (0) 2013.05.09
LING to SQL에서 다중 결과 셋 받기  (0) 2012.11.13
MVC 다중 폼 유효성 체크  (0) 2012.09.07
Razor 구문  (0) 2011.07.19

LING to SQL에서 다중 결과 셋 받기

Posted in .NET Framework // Posted at 2012. 11. 13. 20:04

현재 카테고리에 맞지 않는 영역이지만....

관리도 안될게 뻔한 카테고리를 새로 만들고 싶지 않아 ASP.NET MVC 카테고리에 꺼적인다

 

ASP.NET MVC로 DB 관련 개발할 때,

EntityFramework 나 LING to SQL을 사용하곤 한다

 

근데 이 두 넘은 다 좋은데, 저장 프로시저의 다중 결과 셋을 지원하지 않는다.

 

음.. 지원하지 않는다기 보다는 자동으로 생성되는 designer.cs에만 의존하면 그렇다고 해야 정확한 표현인 듯 싶다.

 

예를 들어,

게시판 글 보기 페이지일 경우, 글 내용과 해당 글에 대한 댓글 정보를 하나의 프로시저에서 두 개의 결과 셋으로 반환하도록 할 경우 처음으로 반환된 결과 셋이 자동으로 바인딩 되는 것이다

 

물론 이런 상황이라면 글 내용은 OUTPUT 변수로 받고 댓글 리스트는 결과셋으로 받아서 해결 가능하다. 실제로 이렇게 사용한 적이 꽤 많다.

 

그러나 문제는 정말로 리스트형태의 결과 셋이 여러 개일 경우이다. 이젠 더 이상 OUTPUT 반환 값에 의존할 수 없게 되었다.

 

저장프로시저의 시나리오는 대략 이렇다.

1. OUTPUT 변수로 몇 가지 값을 반환한다

2. 3개의 결과 셋(레코드 셋이라는 표현을 좋아하는 사람이 있다)도 같이 반환한다

 

그러니깐, 의미적으로 총 3가지 형태의 결과값을 받고 싶은 게다

(OUTPUT 반환 값 + 결과셋1 + 결과셋2 + 결과셋3)

 

물론 결과셋의 개수는 중요치 않다. 두 개 이상의 결과셋을 반환한다는 게 중요하다

 

이제 이 프로시저를 LINK to SQL로 연동해서 결과를 처리하고 싶다

 

먼저 desiner.cs 가 자동 생성한 아래의 코드를 보자

 [global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.USP_GetGameView")]
  public ISingleResult<USP_GetGameViewResult> USP_GetGameView([global::System.Data.Linq.Mapping.ParameterAttribute(Name="GameNo", DbType="SmallInt")] System.Nullable<short> gameNo, [global::System.Data.Linq.Mapping.ParameterAttribute(Name="GameName", DbType="NVarChar(100)")] ref string gameName, [global::System.Data.Linq.Mapping.ParameterAttribute(Name="Score", DbType="Decimal(2,1)")] ref System.Nullable<decimal> score, [global::System.Data.Linq.Mapping.ParameterAttribute(Name="Description", DbType="NVarChar(400)")] ref string description)
  {
   IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), gameNo, gameName, score, description);
   gameName = ((string)(result.GetParameterValue(1)));
   score = ((System.Nullable<decimal>)(result.GetParameterValue(2)));
   description = ((string)(result.GetParameterValue(3)));
   return ((ISingleResult<USP_GetGameViewResult>)(result.ReturnValue));
  }

 

매우 복잡한(?) 코드에 의미 두지 말자. designer.cs 가 자동 생성해 준 코드를 그대로 옮긴 것이니..

그리고 저장프로시저의 원형에 의미도 두지 말자. 그냥 대충 다중 셋을 반환하는 저장프로시저라고 생각하면 된다. 중요한 것은 ISingleResult를 반환한다는 점이다. 그래서 다중 결과 셋의 첫 번째 결과만 바인딩 되는 것이다.

 

그렇다면 해결책은 ISingleResult가 아닌 다중 셋을 반환할 수 있도록 IMultipleResults을 반환하면 된다.

이를 위한 새로운 메서드를 정의해야 하는 데, designer.cs가 자동 생성한 클래스를 사용할 생각은 말아야 한다. DB 연동이 추가/제거/변경 될 경우 이 클래스는 다시 생성되기 때문에 자신의 코드가 모두 사라질 수 있기 때문이다.

 

그렇기 때문에 파티셜 클래스 기법을 이용하면 된다.

 

designer.cs가 생성한 클래스와 동일한 이름으로 파티셜 클래스를 선언하고 다중 셋을 반환할 수 있도록 다음과 같이 작성할 수 있다

public partial class MobileDBDataContext
    {
        [Function(Name = "dbo.USP_GetGameView")]
        [ResultType(typeof(ScreenShot))]
        [ResultType(typeof(Video))]
        [ResultType(typeof(CommentInfo))]       
        public IMultipleResults GetGameView(short? gameNo, ref string gameName, ref decimal? score, ref string description)
        {
            IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), gameNo, gameName, score, description);
            gameName = ((string)(result.GetParameterValue(1)));
            score = ((System.Nullable<decimal>)(result.GetParameterValue(2)));
            description = ((string)(result.GetParameterValue(3)));

            return (IMultipleResults) result.ReturnValue;
        }
    }

 

자동생성된 이전의 코드와 거의 유사한 것을 알 수 있다

다른 점이라면 IMultipleResults 타입을 반환한다는 점과, 다중 셋의 결과를 자동으로 바인딩 하기 위한 클래스 선언이 있다는 점이다.

 

LINQ to SQL을 사용하면서 다중 결과 셋에 대한 목마름이 있었던 사람은 위의 코드만으로도 그 즉시 적용가능하리라 본다.

 

 

 

 

'.NET Framework' 카테고리의 다른 글

Generic DataContract in WCF  (0) 2013.05.09
WCF Data Service VS ASP.NET Web API  (2) 2013.01.08
MVC 다중 폼 유효성 체크  (0) 2012.09.07
Razor 구문  (0) 2011.07.19
ASP.NET Razor  (5) 2010.12.13

MVC 다중 폼 유효성 체크

Posted in .NET Framework // Posted at 2012. 9. 7. 17:10

MVC 기반의 사용자 화면에서는 다중 입력 폼(form)을 허용한다.

사실 허용한다기 보다는 일반적인 HTML 페이지의 동작을 그대로 수용한 것이다

 

이전 개발 환경인 ASP.NET WebForm에서는 서버 측 폼을 기반으로 작업을 많이 했으며 한 페이지에 오직 하나의 서버 폼이 존재할 수 있었다

 

MVC환경에서는 서버 측 폼이라는 개념을 사용하지 않기에 (일반적인 HTML 규칙과 같이) 다중 폼의 사용이 가능한 것이다.

 

그리고 MVC에서는 폼 입력에 대한 유효성 체크와 유효성 메시지 노출을 자동화 시켜 주는 편리한 기능도 있다

 

그런데 이 둘(다중 폼 & 유효성 체크)을 조합해서 사용하다 보면, 의도치 않는 결과를 만나곤 한다

예를들어, 다중 폼 환경에서 특정 하나의 폼의 유효성 체크가 다른 폼의 유효성 체크와 통합되어 표시되는 현상 같은 것이다.

 

두 개의 폼의 입력 필드 이름을 다르게 해서 각각의 입력 컨트롤의 유효성 실패 표시는 쉽게 구분할 수 있으나

@Html.ValidationSummary와 같은 유효성 메시지를 각 폼에 독립적으로 사용하고자 할 때는 기대처럼 동작하지 않는다. (파샬뷰로 분리된 다중 폼이라고 해도 결국 하나로 합쳐지기 때문에 현상은 동일하다)

 

이에 대한 솔루션을 제공하는 아티클이 있다

 

ASP.Net MVC: Validation a on page with multiple forms

 

간단하게 설명하자면,

필드 이름을 폼 마다 (접두어를 두어) 구분하고, 사용자 정의 HtmlHelper를 만들어서 자신의 이름과 일치하는 폼에 대해서만 유효성 체크 Summary(ValidationSummary)를 반환하도록 한 것이다. 그리고 뷰에서는 이 사용자 정의 HtmlHelper를 통해 유효성 메시지를 표시하도록 한다.

 

이러한 접근 방식이 마음에 들지 않는다면, 다음과 같이 설계 할 수 있겠다

 

1) 다중 폼을 사용하지 않기

2) 다중 폼을 사용하되, MVC 유효성 체크에 의존하지 않기

3) 위 아티클의 방식을 나름 응용해서 사용하기

(입력 폼에 자동 모델 바인딩을 사용할 경우, 입력 필드 이름에 의존적이기 때문에 필드 이름을 변경하는 게 불편할 수도 있다)

'.NET Framework' 카테고리의 다른 글

WCF Data Service VS ASP.NET Web API  (2) 2013.01.08
LING to SQL에서 다중 결과 셋 받기  (0) 2012.11.13
Razor 구문  (0) 2011.07.19
ASP.NET Razor  (5) 2010.12.13
웹 리소스 요청 막기, HttpNotFoundHandler  (0) 2010.08.09

Razor 구문

Posted in .NET Framework // Posted at 2011. 7. 19. 10:05
체계적으로 한번은 봐야하는 Razor!. 다른 곳에 집중하느라 아직 제대로 훓어 보지 못하고 있다.
일단 간단한 Razor 구문을 정리해 본다.

1. Razor 코드 블럭
Razor 에서는 골뱅이(@) 키워드가 핵심이다.
과거 ASP.NET 에서 인라인 코드 작성할 때 사용했던 아래와 같은 구문이,
<script runat="server"> ... </script>

Razor에서는 다음과 같이 축약되었다. 과거에 존재했던, 스크립트 선언문과 서버로직이라는 명시가 필요 없어져 매우 간단해졌다
@{ ... }

이 코드 블럭에 닷넷 로직을 구현할 수 있다. 대략 다음과 같이...
@{
int i = 10;
Response.Write(i.ToString());
    }

여기서 한가지 주의할 점은, 코드 블럭을 시작하는 @와 중괄호({) 사이에는 공백이 허용되지 않는다는 것이다.
즉, 아래와 같이 작성하면 런타임 오류를 만나게 된다.
@
{
   ...
}

2. Razor 주석
다른 구문을 보기 전에 주석문 정의를 살펴보자. Razor의 코드 블럭안에서의 주석은, 이전과 동일하게 한줄 주석은 //, 여러줄 주석은 /* ... */ 로 가능하다. 대략 다음과 같다.
@{
        //주석...
        /*
        int i = 5;
        Response.Write(i.ToString());
        */
    }

코드 블럭 바깥에서의 주석 역시 지원하는데 다음과 같이 사용가능하다. @와 *를 같이 사용한다.
@*
       Razor 주석
*@

참고로 당연한 말이지만, Razor 주석은 HTML 주석(<!-- ... -->과는 달리, 서버측 주석이기 때문에 페이지 페이지 랜더링 후, DOM으로 생성되지 않는다.

3. 변수 출력
이전 환경의 ASP.NET 에서는 서버 로직에 구현된 변수를 페이지에 출력하기 위해서는 코드 블럭 안에서는Response.Write 메서드,  코드 블럭 바깥에서는 <%= 변수 %> 구문을 사용했었다. 그러나 Razor 에서는 두 경우 모두 변수에 @만 붙여주면 된다.

@{  
       var str = "Hello, Razor!";  
       @str;
    }

코드 블럭 바깥에서도 단순히 아래와 같이 변수를 출력할 수 있다.
@str

4. Razor 구문 안의 일반 텍스트
만일 Razor 구문안에서 일반 텍스트를 한번에 표현하고 싶을 경우 @: 키워드를 사용할 수 있다.
이전 같으면 변수와 문자열을 + 연산자로 합치고 Response.Write 해야 할 것을 다음과 같이 간단해졌다.
@{      
       var str = "Hello, Razor!";
       @:Plane Text... @str;
   }

5. Razor 구문 안의 마크업 태그
일반 텍스트와는 달리 태그의 경우에는 Razor 구문에서 별다른 키워드 없이 바로 사용가능하다
@{      
       var str = "Hello, Razor!";
      <b>@str</b>
   }

그리고 태그와 함께라면 일반 텍스트도 바로 사용 가능하다.
@{      
       var str = "Hello, Razor!";
      <b>반갑습니다</b>
   }

Razor 엔진에서는 태그를 인식해서 특별한 구문 없이 바로 출력 가능하도록 한 것 같다.

그렇다면 Razor 엔진이 HTML 태그를 모두 기억하고 있는 것일까? 그렇진 않은 것 같다. 다음과 같이 의미없는 태그를 삽입해도 오류 없이 결과를 내뱉는걸 보니, 태그를 기억하는 것이 아니라 태그 기호( <태그>)를 인식하는 듯 하다.
@{      
       var str = "Hello, Razor!";
      <btt>@str;</btt>
   }

한가지 주의할 점은, Razor 구문안에 태그를 혼용할 경우 반드시 여는 태그와 닫는 태그가 쌍으로 존재해야 한다. 다시 말해 다음과 같은 태그 작성은 허용되지 않는다.
@{      
       var str = "Hello, Razor!";
      <b>@str;
   }

6. 조건문
조건문 역시 @ 키워드와 함께 사용할 수 있다. 다음과 같이.
@if(1==1) {
        <font size=@i>@str</font>
    }

7. 반복문
반목문도 사용패턴이 동일하다. 아래 코드는 목록을 만드는 반복문 예를 보여준다.
<ul>
    @for (int i = 0; i < 5; i++)
    {

        <li>@i</li>
    }
  </ul>

동일한 예를 while 반목문으로 처리하는 다음과 같다.
<ul>
    @{
        int i = 0;
        while(i < 5)
        {

            <li>@(i++)</li>
        }
     }
 </ul>

8. 키워드가 아닌 문자로써 '@' 사용하기
Razor 에서는 @ 자체가 키워드이기 문자 출력에 바로 사용할 수 없다. 즉 실제 '@'라는 문자를 출력하고 싶을 경우에는 다음과 같이 @@ 연속해서 두번 써 줘야 한다. 이렇게 하면 Razor 엔진은 @를 키워드로 인식하지 않고 일반 문자로 출력해 준다.
<span>@@난 그냥 골뱅이 문자야</span>


지금까지 아주 기본적인 Razor 구문을 살펴 봤다. 실제 구현시에는 더 많은 상황이 닥칠 것이다.
그럴땐 아래 asp.net 사이트 글을 참조하도록 하자. Razor 구문의 대부분의 규칙을 확인할 수 있다

=> Introduction to ASP.NET Web Programming Using the Razor Syntax

'.NET Framework' 카테고리의 다른 글

LING to SQL에서 다중 결과 셋 받기  (0) 2012.11.13
MVC 다중 폼 유효성 체크  (0) 2012.09.07
ASP.NET Razor  (5) 2010.12.13
웹 리소스 요청 막기, HttpNotFoundHandler  (0) 2010.08.09
ASP.NET MVC, 폼 데이타 전송하기  (0) 2010.08.06

ASP.NET Razor

Posted in .NET Framework // Posted at 2010. 12. 13. 18:24

아... 이 코드의 유연함(?)을 보라!


[출처: 코드파티 ASP.NET Razor 동영상 강의 중...]

과거 ASP의 스파게티가 생각나지 않는가?
ASP.NET의 또 하나의 새로운 시도 Razor(레이저라고 발음)의 코드이다

빠른 개발, 쉬운 개발을 지향하며 탄생한 레이저!
확실히 ASP.NET 웹폼의 그것과는 비교할 수 없이 유연하다

이런 코딩 스타일은 확실히 스파게티 모양새다.
시대가 변하면서 코딩 스타일을 바라보는 시각도 변하는 것 같다.

HTML 코드와 ASP.NET 코드를 철저히 분리하려고 시도하면서 칭송(?) 받았던 ASP.NET 웹폼의 코딩 스타일은 확실히 무거운 느낌이었다.

물론 Razor의 특징을 이 코드블록만으로 설명할 수는 없다.
장점이 분명 있으며 나 역시 프로젝트에 도입을 하려 한다.

그러나 일단 코딩 스타일을 스파게티로 구성할 수 있도록 열어 둔 점은 확실히 시대가 또 변했음을 느낀다

웹 리소스 요청 막기, HttpNotFoundHandler

Posted in .NET Framework // Posted at 2010. 8. 9. 12:11

ASP.NET MVC 모델에서는 (Views 폴더의) 모든 웹 리소스에 대한 직접 요청을 막아 두었다

사용자 화면(뷰)에 해당하는 Views 폴더에 있는 모든 aspx 파일에 대한 다음과 같은 요청은
모두 404 Not Found 로 처리된다
http://yourdomain.com/Views/Index.aspx

이것은 기존 웹폼 모델과 대조되는 면으로,
ASP.NET MVC에서의 사용자 화면(뷰)은 모두 컨트롤러에 의해 선택되고 랜더 되게 하기 위함이다
따라서 리소스에 대한 직접 요청은 의도적으로 막고 있는 것이다

Views 폴더의 Web.config 에 정의된 HttpNotFoundHandler
Visual Studio 에서 ASP.NET MVC 프로젝트를 생성하면 총 두개의 Web.config 가 생성된다
루트에 있는 Web.config 는 기존과 같이 응용프로그램 전역적인 설정 파일이며
Views 폴더의 Web.config 는 Views 폴더에만 적용되는 설정 파일인데, 이 파일에 정의된
HttpNotFoundHandler 가 리소스 직접 요청에 대한 Not Found 처리를 하는 HttpHandler 이다

<httpHandlers>
     <add path="*" verb="*"  type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>

위와같이 정의된 핸들러에 의해 Views 폴더의 aspx 파일을 포함한 모든 직접 요청은 404로 처리된다

참고로 ASP.NET 웹폼 모델에서는 *.aspx 에 대한 처리는 System.Web.UI.PageHandlerFactory 라는
핸들러에 의해 처리되었다

HTTP 핸들러에 대한 개념과 기본 등록된 핸들러 정보는 다음 링크에서 확인할 수 있다
[ASP.NET] HTTP Handler
[ASP.NET] HttpHandler- Demo
[ASP.NET] Machine.config 미리 정의된 HttpHandler


ASPX 파일만 막기
기본 구성으로는 Views 폴더의 모든 리소스에 대한 직접 접근을 막고 있다
경우에 따라서는 Views 폴더에 ASPX 외에 다양한 웹 리소스가 위치할 수 있다

예를 들어 이미지파일이나 css, js 파일, pdf 파일 등을 들 수 있다
이러한 웹 리소스에 대한 직접 요청은 정상적으로 되길 원할 수도 있다
그렇다면 핸들러 정보를 다음과 같이 수정하여 ASPX 파일만 막도록 하면 된다

<httpHandlers>
     <add path="*.aspx" verb="*"  type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>

그러나 ASP.NET MVC 모델에서는 뷰를 제외한 기타 웹 리소스를 Views 폴더에 두지 않기를 권장한다.이미지나 CSS, JS 와 같은 정적이고 공개적인 웹 리소스들은 자동으로 생성된 Content 폴더에 두는 것이다

참고: http://haacked.com/archive/2008/06/25/aspnetmvc-block-view-access.aspx

'.NET Framework' 카테고리의 다른 글

Razor 구문  (0) 2011.07.19
ASP.NET Razor  (5) 2010.12.13
ASP.NET MVC, 폼 데이타 전송하기  (0) 2010.08.06
ASP.NET MVC 에서 요청 매개변수 넘기기  (0) 2010.08.05
ASP.NET MVC, Hello World  (1) 2010.08.04

ASP.NET MVC, 폼 데이타 전송하기

Posted in .NET Framework // Posted at 2010. 8. 6. 16:38

앞서 ASP.NET MVC 에서 요청 매개변수 넘기기 에서는 URL 질의 방식에 ASP.NET MVC이
어떻게 반응하는지 알아 보았다. URL에 매개변수를 전달하는 방식은 GET 요청에 해당한다

이번에는 ASP.NET MVC이  POST 요청을 어떻게 처리하는지 알아 보도록 하자

POST 요청의 보편적인 모습은, HTML 입력 양식을 이용한 폼 데이타 제출이다
이 글에서는 간단한 회원가입 폼을 POST 로 전송하고 이를 처리하는 방법을 다룬다

ASP.NET MVC 프로젝트를 하나 생성하고 컨트롤러에 엑션 메서드를 하나 추가한다

- HomeController.cs 에 액션 메서드 추가하기
public ActionResult RegisterForm()
{            
    return View();
}

- 회원 가입 페이지 (뷰)  생성하기
그리고 이 액션 메서드에 해당하는 뷰를 생성한다
생성된 RegisterForm.aspx 뷰 페이지는 회원가입 입력 양식을 아래와 같이 정의한다
<body>
    <h1>회원가입</h1>    
    <form action="/Home/RegisterForm" method="post">        
        <p>아이디: <input type="text" name="MemberID" /></p>
        <p>비밀번호: <input type="password" name="Password" /></p>
        <p>닉네임: <input type="text" name="NickName" /></p>
        <p>이메일: <input type="text" name="Email" /></p>
        <input type="submit" value="가입하기" />           
    </form>
</body>

전형적인 폼 양식이다. 이제 프로젝트를 빌드하고 페이지를 호출해 보자
http://localhost:11102/Home/RegisterForm




- 모델 정의 하기
아직 폼 데이터를 처리 할 수 있는 것은 아니다. 회원정보에 해당하는 자료형을 모델로 정의해 보자.
이 모델을 기반으로 폼 데이터가 처리될 것이며 결과 뷰역시 이 모델을 기반으로 작성될 것이다

프로젝트의 Model 폴더에 Member.cs 라는 클래스를 만들고 다음과 같이 작성한다
이때 RegisterForm 뷰에서 정의한 폼 요소들의 Name 속성과 일대일 대응하도록 속성명을 정의하자
public class Member
{
    public string MemberID { get; set; }
    public string Password { get; set; }
    public string NickName { get; set; }
    public string Email { get; set; }

   
public void Submit()
{
        //멤버정보를 DB에 입력하는 등의 실제 작업을 처리한다
    }
}

- 컨트롤러 수정하기
그리고 폼 데이타 수신을 위해 컨트롤러를 다음과 같이 수정한다
AcceptVerbs 어트리뷰트를 통해 GET 과 POST 요청을 직접 명시한다
이렇게 하면 get, post 요청에 적절한 액션메서드가 선택될 것이다
즉 동일한 {controller}/{action} 요청이지만 get, post 요청에 따라 (이름은 같지만) 다른 메서드가
호출되는 것이다

또한 각 액션 메서드의 반환 값을 보면,
get 요청에는 기본 뷰인 RegisterForm.aspx 가 랜더되도록 했으며(return View();),
post 요청에는 명시적으로 이름을 지정하여 RegisterComplete.aspx 뷰가 랜더되도록 하였으며
mebmer 객체를 뷰로 전달하고 있다
(return View("RegisterComplete", member))


그리고 post 요청에 해당하는 액션 메서드의 매개변수를 주의깊게 보자
모델에서 정의한 Member 객체를 post 메서드의 매개변수로 취하고 있다

ASP.NET MVC '모델 바인딩 매커니즘'은 폼 전송으로 전달된 데이터의 키(Key) 정보를 바탕으로
Member 객체의 속성명에 일대일 매칭시켜 값을 자동으로 바인딩 시켜 준다

<input type=text name=MemberID>의 값이 Member.MemberID 로 자동 바인딩이 된다는 말이다

public class HomeController : Controller
{
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult RegisterForm()
    {            
        return View();
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult RegisterForm(Member member)
    {
        member.Submit();
        return View("RegisterComplete", member);
    }
}

- 강력한 형식의 뷰 만들기 (회원가입 완료 페이지(뷰) 생성하기)
이제 회원가입 완료 페이지에 해당하는 RegisterComplete 뷰를 생성해 보자
Controller 아무 영역에다 대고 마우스 우클릭 -> Add View 해서 뷰를 생성하는데
아래 그림과 같이 'Create a strongly-typed view' 를 선택해서 모델로 정의했던
Member 클래스를 View data class 로 선택하도록 한다

이렇게 특정 클래스를 기반으로 강력한 형식의 뷰를 만들게 되면
View data class에 정의된 객체를 뷰에 직접 랜더링 할 수 있게 된다


그리고 생성된 RegisterComplete.aspx 는 다음과 같이 작성한다
이 뷰는 강력한 형식으로 생성된 뷰이기 때문에 Model 이라는 키워드를 통해 직접 모델 데이터에
접근할 수 있게 된다
. 아래 코드는 폼 데이터로 전송되어 모델 객체에 바인딩 된 값을 다시 결과 화면에
뿌리고 있는 것이다
<body>    
    <h1>회원가입을 축하드립니다</h1>
    <p>아이디: <%= Model.MemberID %></p>
    <p>닉네임: <%= Model.NickName %></p>
    <p>이메일: <%= Model.Email %></p> 
</body>





'.NET Framework' 카테고리의 다른 글

ASP.NET Razor  (5) 2010.12.13
웹 리소스 요청 막기, HttpNotFoundHandler  (0) 2010.08.09
ASP.NET MVC 에서 요청 매개변수 넘기기  (0) 2010.08.05
ASP.NET MVC, Hello World  (1) 2010.08.04
벌써 ASP.NET MVC 3 ?  (0) 2010.08.03

ASP.NET MVC 에서 요청 매개변수 넘기기

Posted in .NET Framework // Posted at 2010. 8. 5. 13:39

이전 ASP나 ASP.NET 웹폼 모델에서는 URL의 꼬리표에 붙여 있는 매개변수를
Request 객체를 통해전달 받을 수 있었다

즉 다음과 같이 두 개의 매개변수를 URL 에 붙여서 매개변수를 전달하게 되면,
요청 URL: http://yourdomain/main.aspx?param1=value1&param2=value2

아래처럼 매개변수 정보를 취할 수 있다(ASP.NET 기준)
string value1 = Request.QueryString["param1"].ToString();
string value2 = Request.QueryString["param2"].ToString();

그렇다면 ASP.NET MVC 모델에서는 매개변수를 어떻게 전달하고 받는지 알아보도록 하자

기본은 동일하다
ASP.NET MVC 역시 이전 웹폼 모델과 동일한 형태로 매개변수를 넘기고 받을 수 있다

즉 아래와 같이 요청 URL 에 ? 꼬리표로 매개변수를 전달하면
HomeController 의 Index 액션 메서드에서 다음과 같이 매개변수에 접근할 수 있게 된다

요청 URL: http://localhost:11102/Home/Index/?name=Park&age=10

public
ActionResult Index()
{
    string name = Request.QueryString["name"].ToString();
   string age = Request.QueryString["age"].ToString(); 

    ViewData["param"] = String.Format("Hello, {0}, Your age {1}", name, age);

    return View();
}


액센메서드의 매개변수로 전달 받기
URL 에 포함되어 있는 매개변수를 액션 메서드의 매개변수로 전달 받을 수도 있다
아래 코드는 Index 매개변수를 통해 URL로 전달되는 매개변수를 전달 받고 있다


요청 URL: http://localhost:11102/Home/Index/?name=Park&age=10

public
ActionResult Index(string name, string age)

{

    ViewData["param"] = String.Format("Hello, {0}, Your age {1}", name, age);

    return View();

}

액션 메서드를 통해 매개변수를 전달 받는 것은 ASP.NET MVC 프레임워크에서 제공해 주는 기능이며
이전 형태 보다는 조금 더 ASP.MVC 스럽다고 할 수 있겠다

액션 메서드로 매개변수를 전달 받을 경우 URL 에 정의된 매개변수 명과 액션메서드에 정의된
매개변수 명이 동일해야 한다는 규칙이 있다(순서는 상관 없다. 변수 이름이 중요하다)



참고로 URL 에 매개변수가 있다고 해서 반드시 액션 메서드에 매개변수를 정의 해야 하는 것은 아니다
그리고 그 반대의 경우, 즉 액션메서드에 매개변수가 정의 되었다고 해서 반드시 URL에 매개변수를 넘겨야 하는 것은 아니다 (물론 이 두 경우는 매개변수를 사용하지 않을 경우이다)

예를 들어 URL 에 매개변수가 생략되었을 경우 액션메서드로는 null 을 전달하게 될 것이다


URL 스키마 사용자 정의
앞서 Request.QueryString 으로 매개변수를 가져오는 방식에 비해 액션 메서드의 매개변수로 가져오는 것이 보다 깔끔하고 직관적이라는하다는 것을 느낄 수 있다.
이것은 매개변수를 가져오는 측면에서 ASP.NET MVC 가 지원해 주는 부분이 되겠다

이번에는 매개변수를 전달하는 측면에서의 ASP.MVC 의 장점을 살펴 보자

앞서 요청 URL은 다음과 같은 스키마로 이루어 졌었다
http://localhost:11102/Home/Index/?name=Park&age=10

전통적인 방식으로 URL에 ? 꼬리표를 통해 매개변수를 전달하고 있다
우리는 이러한 요청 URL을 보다 깔끔하게 변경하고 싶다

ASP.NET MVC 에서는 URL 스키마를 개발자의 입맛대로(?) 재 구성할 수 있는 방법을 제공해 준다.

이는 ASP.NET MVC의 라우팅 엔진이 제공해 주는 기능인데,
Global.asax 의 RegisterRoutes 메서드의 커스트마이징을 통해 구현할 수 있다

우선 매개변수를 어떤 형태의 URL 스키마로  정의할 지 결정해야 하는데,
다음과 같이 '/(슬래쉬)'를 계속 이어가고 싶다고 가정해 보자

http://localhost:11102/Home/Index/매개변수1/매개변수2

이와 같은 URL 스키마를 실현하기 위해 Global.asax 파일에 정의된
RegisterRoutes 메서드를 약간 수정해 보자

public static void RegisterRoutes(RouteCollection routes)

{

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 

    routes.MapRoute(

        "Default",

 

        "{controller}/{action}/{name}/{age}", // URL with parameters

 

              new { controller = "Home", action = "Index",

name = UrlParameter.Optional,

                         age = UrlParameter.Optional }

  );

}


MapRoute 메서드의 두 번째 정보에 URL 스키마를 정의한다
우리가 결정한 URL 스키마를 실현하기 위해 /(슬래시)로 매개변수를 취할 수있도록 하였다
컨트롤러/액션메서드/name매개변수/age매개변수 형태로 정의함
=> "{controller}/{action}/{name}/{age}"


그리고 다음과 같이 각 매개변수의 옵션 여부를 지정하였다
이는 name, age 정보가 URL에서 생략될 수 있다는 의미이다
name = UrlParameter.Optional, 
age = UrlParameter.Optional

이제 구성이 완료 되었으니 다음과 같은 요청에 정상적으로 반응하게 될 것이다
http://localhost:11102/Home/Index/Park/10


만일 다음과 같은 URL을 원한다면,
http://localhost:11102/Home/Index/Park-10

다음과 같이 정의하면 된다
"{controller}/{action}/{name}-{age}"

참고로 이런 식으로 URL 스키마 재정의로 매개변수 정보를 구성하였다면,
더 이상 Request.QueryString 로 받을 수 없다는 것에 주의하자
-----------------------------------------------------------------------------------

마지막으로 URL 재구성된 상태에서 전통적인 ? 매개변수 전달을 같이 혼용하면 어떻게 될까?
다시 말해 다음과 같은 URL 요청은 어떻게 되는지 살펴 보자

요청 URL: http://localhost:11102/Home/Index/Park/10?name=Kim&age=20

액션메서드:
public
ActionResult Index(string name, string age)

{

    ViewData["param"] = String.Format("Hello, {0}, Your age {1}", name, age);

    return View();

}

이를 경우 URL 로 재구성된 매개변수 정보가 먼저 참조 된다
즉 URL 요청으로 부터 전달되는 name, age 라는 매개변수가 각각 두 개씩 이지만
URL 재구성에 의한 name=Park, age = 10 이 전달되는 것이다

그러나 만일 URL 스키마에서 매개변수 정보를 생략하고 다음과 같이 요청 된다면,
http://localhost:11102/Home/Index/?name=Kim&age=20

결과는 ?  매개변수가 전달되게 된다

ASP.NET MVC 는 라우팅 구성 정보에 의거한 적절한 매개변수를 가져올 수 없는 경우
전통적인 ? 질의 문자열에서 매개변수를 가져오게 되는 것이다