분류

TapMap 파트 1: Couchbase Server 2.0 및 .NET 클라이언트 라이브러리를 사용하여 T 리포지토리 작업하기

저는 .NET과 Couchbase Server를 사용하기 위한 참조 애플리케이션을 만들어야겠다고 생각했습니다. Progressive NoSQL 컨퍼런스에 참석하기 위해 런던으로 이동하는 동안 중요한 기술호텔 방이 준비되기를 기다리는 동안 스타트벅스에서 TapMap이라는 애플리케이션의 코드를 작성하며 시간을 보냈습니다.

이 앱은 매우 간단합니다. 맥주에 특별히 초점을 맞춘 트위터의 모조품일 뿐입니다. 기본 아이디어는 술집에 있는 누군가가 특정 장소에서 특정 맥주를 발견하고 마셨다고 신고하는 것입니다. 이러한 신고를 "탭"이라고 합니다. "지도" 기능은 지오카우치를 사용하여 노키아 지도에서 일련의 탭을 렌더링합니다. 사용자는 자신이 좋아하는 맥주를 어디서 찾을 수 있는지 빠르게 알 수 있다는 장점이 있습니다!

코드는 아직 알파 버전이고 제 HTML 기술도 업그레이드해야 합니다. 하지만 충분히 구현을 마쳤기 때문에 탭맵에 노출된 코드 패턴과 솔루션에 대한 일련의 포스팅을 시작하려고 합니다. 이 글은 1편으로, .NET 클라이언트 라이브러리를 사용하여 간단한 T for Couchbase 리포지토리를 만드는 방법을 설명합니다. 코드는 다음에서 다운로드할 수 있습니다. https://github.com/couchbaselabs/TapMap.

리포지토리에 익숙하지 않은 경우 기본 개념은 도메인 객체의 데이터 액세스를 담당하는 클래스가 있다는 것입니다. 따라서 이메일 주소로 사용자를 쿼리하려면 UserRepository의 GetByEmail(문자열 이메일) 메서드를 사용하면 됩니다. 리포지토리에는 일반적으로 표준 CRUD 메서드도 포함되어 있습니다. 이 패턴에는 조금 더 많은 것이 있지만 기본적인 사용법을 보여드리겠습니다. 자세한 내용은 https://msdn.microsoft.com/en-us/library/ff649690.aspx.

상속은 코드 재사용을 위한 자연스러운 방법입니다. 탭맵은 기본 리포지토리 클래스를 사용하여 서브클래스에 CRUD 메서드를 제공합니다. 또한 베이스 클래스는 제네릭을 사용하여 CRUD 메서드가 객체 대신 T의 인스턴스를 반환할 수 있도록 합니다. T를 특정 유형으로 제한하면 곧 설명할 몇 가지 추가적인 이점도 얻을 수 있습니다.

public 초록 클래스 리포지토리베이스<T> 어디 T : 모델베이스
{
//크러드 메서드
}

이미 알고 계시겠지만 Couchbase Server 2.0은 문서에 스키마를 적용하지 않습니다. 포함하려는 모든 속성과 함께 JSON 문서를 저장합니다. 즉, 모든 문서는 해당 키에서 값을 파생하는 예약된 "_id" 속성과 함께 저장됩니다.

다음과 같이 문서를 추가합니다:

클라이언트.스토어(스토어모드.추가, "맥주_세_철학자", “{ 이름 : 세 명의 철학자 }”);

그 결과 다음과 같은 문서가 생성됩니다:

{
"_id" : "맥주_세_철학자",
"name" : "세 철학자"
}

따라서 모든 또는 모델 객체가 이 ID 속성을 갖도록 하기 위해 ID 속성을 정의하는 모델 베이스 클래스를 생성합니다.

[직렬화 가능]
public 초록 클래스 모델베이스
{
[제이슨 프로퍼티(속성 이름 = "_id")]
public 문자열 Id { get; set; }
}

여기서는 JsonProperty에 대해 자세히 설명하지 않겠습니다. 저는 직렬화를 위해 Json.NET을 사용하고 있으며, 이에 대한 자세한 내용은 https://www.couchbase.com/develop/net/next. 중요한 점은 "Id" 속성을 가진 모델 객체가 있고 리포지토리가 ModelBase의 서브클래스를 사용하도록 제한함으로써 항상 이 속성을 갖게 된다는 것입니다.

보기를 만들 때 모든 '맥주' 문서나 모든 '사용자' 문서를 쉽게 찾거나 특정 이메일 주소로 '사용자' 문서를 찾을 수 있도록 문서에 분류 체계를 두는 것이 유용합니다. 이 요구 사항을 용이하게 하기 위해 모든 모델 문서에 "type" 속성을 부과할 것입니다. 모델베이스에 읽기 전용의 추상적인 Type 속성을 추가하겠습니다.

[제이슨 프로퍼티("type")]
public 초록 문자열 유형 { get; }

이제 모델에 대해 가정할 수 있는 가정을 이해했으므로 RespositoryBase 클래스로 돌아가 보겠습니다.

RepositoryBase 클래스에서 가장 먼저 해야 할 일은 CouchbaseCliet의 인스턴스를 정의하는 것입니다.

보호됨 정적 읽기 전용 카우치베이스클라이언트 _클라이언트 = null;

정적 리포지토리베이스()
{
클라이언트 = new 카우치베이스클라이언트();
}

클라이언트 설정은 비용이 많이 들기 때문에 정적 생성자에서 인스턴스화되는 읽기 전용 정적 인스턴스를 생성하여 AppDomain당 한 번만 발생하도록 할 것입니다. 클라이언트는 기본 클래스의 기본 CRUD 메서드를 확장해야 하는 서브클래스에 보호된 필드로 노출됩니다. 이 클라이언트는 "couchbase" 섹션을 정의한 Web.config에 의존한다는 점에 유의하세요. 언젠가는 IoC 친화적인 버전의 RepositoryBase를 추가할 것입니다.

리포지토리 베이스는 생성, 업데이트 및 저장 메서드를 정의합니다. 이러한 메서드는 차례로 적절한 StoreMode 값으로 비공개 "store" 메서드를 호출합니다. Create와 Update는 간단합니다. Save는 키가 존재하지 않을 때 문서를 생성하고 키가 있으면 대체합니다. 반환 값은 연산에 대한 CAS 값입니다. 언젠가는 CAS를 선택적 매개변수로 추가할 것입니다. 저장 메서드는 또한 모든 객체를 Couchbase에 저장하기 전에 모든 객체를 JSON 문자열로 직렬화하는 CasJson 확장 메서드를 호출합니다.

public 가상 ulong 만들기(T 모델)
{
반환 store(스토어모드.추가모델);
}

public 가상 ulong 업데이트(T 모델)
{
반환 store(스토어모드.교체모델);
}

public 가상 ulong 저장(T 모델)
{
반환 store(스토어모드.설정모델);
}

비공개 ulong store(스토어모드 모드, T 모델)
{
var 결과 = 클라이언트.CasJson(모드, BuildKey(모델)모델);
반환 결과.결과 ? 결과.Cas : 0;
}

또한 비공개 저장소 메서드는 보호된 BuildKey 메서드를 사용하여 규칙에 따라 키를 생성합니다. 구체적으로, 키는 모델의 Type 속성과 Id 속성에서 공백을 밑줄로 대체하여 구성됩니다. 이 버전의 BuildKey는 Create, Update 또는 Save를 사용하는 서브클래스에서 재정의되기 때문에 실제로 TapMap은 이 버전을 사용하지 않습니다. 유형과 이름을 기반으로 키를 구성할 수 있도록 모델베이스에 "이름" 속성을 추가하는 것을 고려 중입니다. 예를 들어 이름이 "Old Yankee Ale"인 맥주는 "beer_Old_Yankee_Ale" 키를 갖게 됩니다.

보호됨 가상 문자열 빌드 키(T 모델)
{
반환 문자열.Concat(모델.유형, “_”모델.Id.교체(” “, “_”));
}

Get 메서드는 호출 시 문서의 _id를 인수로 받습니다. 그런 다음 이 값은 클라이언트의 일반 Get 메서드를 호출하기 위한 키로 사용됩니다. 또 다른 확장 메서드인 GetJson(문자열 id)도 Get 메서드에서 JSON 문서를 T의 인스턴스로 변환하는 데 사용됩니다.

public 가상 T Get(문자열 id)
{
var doc = 클라이언트.GetJson<T>(id);
만약 (doc != null) doc.Id = id; //서버가 JSON에서 _id를 다시 전달하지 않습니다.
반환 doc;
}

제거는 매우 간단합니다. 문서 키(다시 _id)를 가져와서 Couchbase에서 항목을 제거하기만 하면 됩니다.

public 가상 void 제거(문자열 id)
{
클라이언트.제거(id);
}

보기 액세스에 대한 기본 지원은 보기 메서드에서 관례에 따라 수행됩니다. 이 메서드는 디자인 문서 이름이 복수형 모델 유형 이름이라고 가정합니다. 따라서 BeerRepository는 자동으로 보기 요청을 "beers"라는 디자인 문서에 매핑합니다. View 메서드는 IView 인스턴스를 반환합니다. 호출자가 IView를 이터레이트할 때까지 HTTP 호출이 실제로 이루어지지 않으므로, 서브클래스는 유창한 쿼리 메서드를 사용하여 뷰를 호출할 수 있습니다. 곧 특정 뷰 호출의 예를 보여드리겠습니다.

보호됨 IView<IViewRow> 보기(문자열 보기 이름)
{
반환 클라이언트.GetView(인플렉터넷.복수화(typeof(T).이름.ToLower()), 보기 이름);
}

현재 GeoCouch를 사용하여 지리공간 쿼리를 허용하는 SpatialView 메서드도 있습니다. 클라이언트의 GetSpatialView 메서드는 현재 실험 중이며 클라이언트 1.2 마스터 브랜치에 추가되지 않았습니다. 하지만 곧 추가될 예정입니다. TapMap 저장소를 복제하면 이 메서드를 지원하는 클라이언트 바이너리를 얻을 수 있습니다.

보호됨 IView<ISpatialViewRow> 스페이셜뷰(문자열 보기 이름)
{
반환 클라이언트.GetSpatialView(인플렉터넷.복수화(typeof(T).이름.ToLower()), 보기 이름);
}

여기까지 RepositoryBase에 대해 설명했습니다. 이제 메서드와 목적을 살펴봤으니 하위 클래스에 대해 자세히 알아보겠습니다. 먼저 사용자 모델 클래스는 사용자 이름, 이메일 및 비밀번호에 대한 속성을 정의합니다. 또한 클래스 프로퍼티의 소문자 버전에 대한 JSON 프로퍼티 매핑도 제공합니다. User는 ModelBase를 확장하고 문자열 "user"를 반환하여 Type 속성을 구현한다는 점도 주목하세요.

[직렬화 가능]
public 클래스 사용자 : 모델베이스
{
[필수]
[제이슨 프로퍼티("username")]
public 문자열 사용자 이름 { get; set; }

[필수]
[제이슨 프로퍼티("이메일")]
public 문자열 이메일 { get; set; }

[필수]
[제이슨 프로퍼티("비밀번호")]
public 문자열 비밀번호 { get; set; }

public 오버라이드 문자열 유형
{
get { 반환 "user"; }
}
}

UserRepository 클래스는 T가 사용자 유형으로 설정된 RepositoryBase를 확장합니다.

public 클래스 사용자 저장소 : 리포지토리베이스<사용자>
{
//methods
}

위의 간단한 정의만으로도 UserRepository는 상당한 힘을 가지고 있습니다. 기본 클래스에 정의된 모든 기본 CRUD 기능을 수행할 수 있습니다. 그러나 앱에서 사용자 문서로 작업하는 몇 가지 특수한 시나리오가 있으며 UserRepository는 이러한 시나리오를 지원하기 위한 메서드를 추가합니다.

사용자가 사용자 이름과 비밀번호로 로그인할 때 사용자 이름과 비밀번호의 복합 키를 생성하는 보기를 만드는 것도 한 가지 옵션이 될 수 있습니다. 그러나 더 나은 대안은 사용자 이름과 비밀번호를 사용하여 문서의 키를 작성하는 것입니다. 이러한 키를 사용하면 사용자가 로그인할 때 사용자 문서를 쉽게 검색할 수 있습니다. 이 접근 방식은 보조 보기 인덱스를 만들 필요가 없어 공간을 절약하고 해시 테이블 조회를 사용하여 키를 더 빠르게 만들 수 있습니다.

재정의된 Create 메서드에서 사용자 이름(Id)과 비밀번호(해시됨)는 user_Username_HashedPassword 형식의 키를 만드는 데 사용됩니다.

public 오버라이드 ulong 만들기(사용자 모델)
{
모델.비밀번호 = 해시헬퍼.ToHashedString(모델.비밀번호);
모델.Id = 빌드 키(모델);
var 결과 = 클라이언트.CasJson(스토어모드.추가, 빌드키(모델)모델);
반환 결과.결과 ? 결과.Cas : 0;
}

새 키 형식을 허용하기 위해 BuildKey도 재정의됩니다. 빌드키 비공개 메서드는 곧 보게 될 오버로드된 Get 메서드에 필요합니다.

보호됨 오버라이드 문자열 빌드 키(사용자 사용자)
{
반환 빌드키(사용자.사용자 이름, 사용자.비밀번호);
}

비공개 문자열 빌드키(문자열 사용자명, 문자열 비밀번호)
{
반환 문자열.Concat("user_", 사용자 아이디, “_”비밀번호);
}

RepositoryBase에 정의된 Get 메서드는 키로 조회한다고 가정합니다. 특수한 사용자 키 형식을 호출하는 UserController 클래스에 노출하는 대신 새 키가 구성되는 사용자 이름과 비밀번호를 받도록 Get이 오버로드됩니다.

public 사용자 가져오기(문자열 사용자명, 문자열 비밀번호)
{
반환 Get(빌드키(사용자명, 해시헬퍼.ToHashedString(비밀번호)));
}

사용자 저장소 클래스는 사용자 이름이나 이메일이 고유한지 여부에 대한 질문에 답하는 데 도움을 주는 역할도 담당합니다. 이를 위해 두 가지 뷰가 사용됩니다. 첫 번째는 단순히 모든 사용자 문서의 키로 이메일을 내보내고, 두 번째는 사용자 이름을 키로 내보냅니다. 이러한 인덱스를 만드는 더 영리한 방법도 있지만, 간단하게 하기 위해 검사할 속성과 해당 보기 사이에 일대일 매핑이 있습니다.

함수 (doc) {
만약 (doc.유형 == "user") {
emit(doc.이메일, null);
}
}

함수 (doc) {
만약 (doc.유형 == "user") {
emit(doc.usrename, null);
}
}

사용자 저장소에는 기본 뷰 메서드를 사용하여 각각의 뷰를 쿼리하는 GetByEmail 및 GetByUsername 메서드가 정의되어 있습니다. 두 메서드 모두에서 제한은 1로 설정되어 있으며, 이 속성 값을 가진 단일 항목이 존재하는지 여부만 확인하므로 첫 번째 반복에서는 반환이 어렵습니다. 데이터의 고유성을 보장하고 가능한 한 최신의 뷰 인덱스를 유지해야 하므로 오래된 뷰는 허용되지 않습니다.

public 사용자 GetByEmail(문자열 이메일)
{
foreach (var item in 보기("by_email").제한(1).(이메일).Stale(StaleMode.False))
{
반환 Get(항목.ItemId);
}
반환 null;
}

public 사용자 이름 가져오기(문자열 사용자 이름)
{
foreach (var item in 보기("by_username").제한(1).(사용자 이름).Stale(StaleMode.False))
{
반환 Get(항목.ItemId);
}
반환 null;
}

이 모든 것이 어떻게 결합되는지 보여드리기 위해 나중에 UserController를 살펴보겠습니다. 이 포스팅의 목표는 RepositoryBase와 이를 확장하는 방법을 소개하는 것이었습니다.

이 문서 공유하기
받은 편지함에서 카우치베이스 블로그 업데이트 받기
이 필드는 필수 입력 사항입니다.

작성자

게시자 존 자블로키

존 자블로키는 NET. SDK 개발자입니다. John은 Beantown ALT.NET의 주최자이자 Fairfield University의 전 겸임교수이기도 합니다. Amazon에서 Couchbase Server를 설치하고 구성하는 방법을 설명하는 "Couchbase Essentials"라는 책을 확인할 수도 있습니다.

댓글 남기기

카우치베이스 카펠라를 시작할 준비가 되셨나요?

구축 시작

개발자 포털에서 NoSQL을 살펴보고, 리소스를 찾아보고, 튜토리얼을 시작하세요.

카펠라 무료 사용

클릭 몇 번으로 Couchbase를 직접 체험해 보세요. Capella DBaaS는 가장 쉽고 빠르게 시작할 수 있는 방법입니다.

연락하기

카우치베이스 제품에 대해 자세히 알고 싶으신가요? 저희가 도와드리겠습니다.