SignalR 이란?

  • 최신 ASP.NET에 추가된 라이브러리.

이것을 사용하면 리얼타임, 비동기, 쌍방향 통신(Push나 RPC) 기능을 Web 애플리케이션에 쉽게 사용할 수 있다.

  • NuGet을 통해서 입수할 수 있다. Open Source
  • 클라이언트/서버 간에 영속적 연결. 일반 웹은 비 영속적 연결
  • 트랜스포트를 자동으로 네고에이션.

웹 서버나 클라이언트에서 따라서 WebSocket, Server-Send Events, Forever Frames, Long Polling 사용

  • 하나의 서버 당 수천의 접속을 비동기로 처리한다.
  • 비슷한 기술로 Node.js가 있다.
  • ASP.NET 개발팀의 David Fowler, Damian Edwards가 개인 프로젝트로 시작했다가 정식으로 ASP.NET에 들어갔음.

 

 

Sample

  • ASP.NET SignalR Stock Ticker Sample

http://chackr.azurewebsites.net/SignalR.Sample/StockTicker.html

  • SignalR

ShootR https://github.com/NTaylorMullen/ShootR

 

RPC 통신

  • WebSocket, HTTP 위에서 동작하는 RPC(리모트 프로시져 콜)을 구현.
  • 클라이언트에서 서버에 만들어진 함수를 호출하는 느낌으로 서버측 API를 실행할 수 있다.
  • SignalR은 클라이너트에서 호출 가능한 함수 집합을 Hub라고 부른다.

클라이언트에서는 허브의 프록시 오브젝트를 만들어서 함수를 호출하는 형식이다.

  • 물론 함수의 반환 값도 얻을 수 있다. 다만 통신은 비동기 이므로 반환 값은 JQuery로 만들어진 Deferred 오브젝트를 사용하여 얻는다.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

<script type="text/javascript">

   $(function() {

      var connection = $.hubConnection();

      var sample = connection.createHubProxy("sample");

   

      connection.start(function () {

        sample.invoke("Say", "Hello, world");

      });

   })

</script>

   

<script type="text/javascript">

    $(function() {

        var connection = $.hubConnection();

        var sample = connection.createHubProxy("sample");

   

        connection.start(function () {

            sample.invoke("Say", "Hello, world").done(function(result) {

                alert(result);

            });

        });

    })

</script>

 

]PersistentConnection Hub

  • PersistentConnection Hub 개의 API 있다.
  • PersistentConnection 이름대로 서버와 클라이언트 간의 '지속적인 접속' 제공하면서 클라이언트를 관리하는 기능을 가지고 있다. 통신 기능면에서는 하나의 메시지를 송수신하는 기능 밖에 없는 레벨 API 이다.
  • Hub PersistentConnection 위에 구현된 것으로 레벨 API이다.

클라이언트/서버 간의 함수 호출이라는 기본적인 기능을 제공한다. HubPiprline 이용하여 클라이언트 인증을 구현. 물론 자체 확장도 가능하다.

  • 보통 PersistentConnection 보다 Hub API 사용한다.
  • Hub RPC 사용하기 때문에 PersistentConnection 비교하면 리퀘스트 해석이나 리플렉션을 사용하기 때문에 오버헤드가 크다.
  • 성능을 원한다면 PersistentConnection, 빠른 개발을 원한다면 Hub 사용한다.
    Hub에 정의한 RPC는 비동기로 호출되므로 공유 리소스는 스레드 세이프 하지 않다

Hub

  • 구현

  • Hub 클래스에 구현되어 있는 프로퍼티
    • Context

현재 요청 정보를 유지

  •  
  • Groups

그룹을 관리하는 클래스

  •  
  • Clients

SignalR에서 관리하고 있는 모든 클라이언트를 뜻한다

  •  
  • Caller

리퀘스트를 요청한 클라이언트를 뜻한다.

 

서버와 클라이언트 접속 흐름

  • SignalR 클라이언트 관리

클라이언트 마다 유일한 커넥션 ID 발행 

  • 서버에 준비된 함수를 호출한다

클라이언트는 반환 값을 얻을 있다 

  • 클라이언트에 등록된 함수를 호출한다.

서버는 반환 값을 얻을 없다. 

  • 클라이언트를 그룹화 하여 관리

  • 그룹에 접속을 추가하는 것을 구현

  • 그룹에 함수 호출

 

다양한 클라이언트 지원

  • SignalR JavaScript 이외의 클라이언트에서도 사용할 있다.
  • Windows 8 RT, .NET Framework, iOS, Mac

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class Program

{

    static void Main(string[] args)

    {

        Start();

    }

   

    private static async void Start()

    {

        var connection = new HubConnection("http://example.com/signalr");

        var sample = connection.CreateHubProxy("sample");

   

        await connection.Start();

   

        var result = await sample.Invoke<string>("Say", "Hello, world");

   

        Console.WriteLine(result);

    }

}

 

 

개발 환경

  • Visual Studio 사용한다.
  • Visual Studio 2012 IIS Express WebSocket 대응.
  • SignalR IIS 아닌 일반 .NET 애플리케이션에서 서버로 사용 가능.
  • 물론 무료용 Visual Studio에서도 개발할 있다.
  • Windows Azure Web 사이트에 배포 가능
  • WebSocket 사용하기 위해서는 Window Server 2012(Windows 8) 이상에서 지원하는 최신 IIS + .NET Framework 4.5 이상을 사용해야 한다.

 

Scale Out

  • 영속적인 연결을 인스턴스 마다 생성
    • 단순하게 수를 늘리는 것만으로 대응할 없다

  • 인스턴스간에 접속을 공유해야 한다
    • 메시징을 이용한다

  • SignalR Scale Out 하기 위한 방법으로 Redis, ServiceBus (Azure), SQL Server 사용한다.

모두 NuGet 통해 입수 있다.

 

Scale Out - Pub/Sub 메시징

  • Windows Azure 서비스 버스
    • 메시지 흐름

 

 

SignalR vs Node.js

  • 기능적으로 비슷하다.
  • Node.js JavaScript 사용하고, SignalR C# Visual Basic.NET 사용.
  • Windows 플랫폼 프로그래머, 한국의 게임 프로그래머에게는 SignalR 사용이 쉬움.
  • JavaScript 알고 있거나 혹은 앞으로 자주 사용한다면 Node.js 좋은 선택이 되지만 그렇지 않다면 (게임 프로그래머의 경우) C# Windows 플랫폼 사용이 훨씬 쉽기 때문에 SignalR 선택하는 것이 좋음.
  • Windows 라이센스 문제는 로직 부분에만 사용하면 문제가 안되고, 특히 클라우드 플랫폼을 사용한다면 크게 문제 되지 않음. Linux 그냥 사용할 있는 쉬운 OS 아님.
  • Node.js npm 통해 풍부한 외부 라이브러리를 사용할 있고, SignalR NuGet 통해 풍부한 외부 라이브러리를 사용할 있다.
  • SignalR Mono 사용하여 Linux에서 사용할 있다.

 

SignalR Node.js 비교표

 

SignalR async/await

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

[HubName("web")]

public class WebHub : Hub

{

    public string GetTitle(string url)

    {

        var client = new WebClient();

   

        var contents = client.DownloadString(url);

   

        var match = Regex.Match(contents, @"<title>(.*)<\/title>");

   

        return match.Success ? match.Groups[1].Value : "";

    }

}

   

   

[HubName("web")]

public class WebHub : Hub

{

    public async Task<string> GetTitle(string url)

    {

        var httpClient = new HttpClient();

   

        var contents = await httpClient.GetStringAsync(url);

   

        var match = Regex.Match(contents, @"<title>(.*)<\/title>");

   

        return match.Success ? match.Groups[1].Value : "";

    }

}

 

SignalR 장점

  • 간단한 클라이언트/서버 API
  • 비동기멀티 스레드로 동작
  • 최신최적의 통신 방법을 자동으로 선택
  • Scale Out 아주 쉬움
  • 클라이언트 그룹 관리 제공
  • 프로그래머는 로직 구현에만 집중할 있다

 

Signal 프로젝트 생성

기타

  • a.html 에서 연결을 b.html 이동하면 연결이 끊어진다.
    • SignalR 하나의 웹페이지에서 관련 작업을 해야 한다.
    • 다만 ASP.NET RAZOR 같은 것을 사용하면 하나의 html 페이지에서 복수의 html 페이지를 포함해서 파일에서 구현하는데 문제 없음

 

\

 

글은

http://www.atmarkit.co.jp/ait/articles/1303/19/news099.html 여기의 자료를 많이 참고했습니다.

 

 

참고

  • SignalR

https://github.com/SignalR/SignalR 
wiki https://github.com/SignalR/SignalR/wiki

  • MSDN

http://msdn.microsoft.com/ko-kr/library/jj943739(v=vs.111).aspx 
http://www.asp.net/signalr

  • Sample

https://github.com/SignalR/Samples

  • SignalR Online Counter Sample

http://www.codeproject.com/Articles/536514/SignalRplusOnlineplusCounterplusSample

  • SignalR - Simple Chat Application in C#

http://www.codeproject.com/Articles/524066/SignalR-Simple-Chat-Application-in-Csharp

  • ASP.NET SignalR 시작하기 (C#)

http://www.taeyo.pe.kr/Columns/View.aspx?SEQ=458&PSEQ=35

신고
by 흥배 2013.12.16 10:16
저작자 표시
신고
by 흥배 2013.12.12 08:00
저작자 표시
신고
by 흥배 2013.12.06 08:00

C#에서 키워드를 식별자로 사용하고 싶을 때 verbatim identifier라는 것을 사용한다.

verbatim identifier는 식별자로 사용하고 싶은 키워드의 앞에 @을 붙이는 것이다.

 

예를 들면 int를 식별자로 사용하고 싶다면

var @int = 10;

 

@는 특별한 의미를 가지지 않으므로 아래와 같은 경우는 에러가 된다.

var @i = 10;

var i = 20;

에러 이유는 i를 중복 정의했기 때문이다.

 

verbatim identifier을 사용하는 경우는 대부분

- 다른 언어와 연동

- 자동 생성

 

 

ASP.NET의 경우 html class라는 식별자와 구분하기 위해 사용한다.

<%: Html.ActionLink("", "Index", null, new { @class = "Navigator" }) %>

 

 

참조: http://bleis-tift.hatenablog.com/entry/20101208/1291817386

저작자 표시
신고
by 흥배 2013.12.04 12:38

C++에서는 로그를 남기기 위해 로그 라이브러리를 만들 때 꼭 사용하는 것이 가변인수와 매크로입니다.

가변인수를 사용하여


void LogMessage( LPCTSTR format, ... )

{

char SBody[ MAX_PATH ] = { 0, };

va_list strlist;

va_start( strlist, format );

_vsntprintf_s( SBody, _countof(SBody), _TRUNCATE, format, strlist );

va_end( strlist );

}



이런 식으로 하나의 메시지를 만들고

이 LogMessage함수를 어디서, 언제 호출했는지 알리는 방법은 __FILE__, __LINE__, __FUNCTION__ 와 같은 매크로를 사용합니다.


이런 기능을 닷넷에서 구현하려면 가변인수는 다음과 같은 방식으로 구현 합니다. 


public void print(int i, params string[] messages)

{

string logmsg;


foreach (string msg in messages)

logmsg += msg;


}



그리고 __FILE__, __LINE__, __FUNCTION__ 매크로는 닷넷의 어트리뷰트를 사용합니다.

using System.Runtime.CompilerServices;

using System.Diagnostics;


void LogToUI(string logText,

[CallerFilePath] string fileName = "",

[CallerMemberName] string methodName = "",

[CallerLineNumber] int lineNumber = 0)

{

string logmsg = string.Format("[줄:{0}|호출:{1}|시간{2}] : {3}", lineNumber, methodName, DateTime.Now.ToShortTimeString(), logText);

listBoxLog.Items.Add(logmsg);

}


// 사용 

LogToUI("서버 생성 성공");

저작자 표시
신고
by 흥배 2013.12.02 08:00
  • 오픈소스 http 클라이언트 라이브러리
  • 공식 사이트 http://restsharp.org/
  • GitHub https://github.com/restsharp/RestSharp
  • 소개 http://pawel.sawicz.eu/restsharp/
  • 예제 http://stackoverflow.com/questions/10226089/restsharp-simple-complete-example




1) 시작 번호 + 크기까지의 숫자범위를 문자로 얻는다.

var client = new RestSharp.RestClient(ServerConfig.LogServerURI); //http://localhost:10301/GameService 

string RestAPI = string.Format("{0}/{1}", "RequestRangeNumber, 10);

var request = new RestSharp.RestRequest(RestAPI, RestSharp.Method.GET);

var queryResult = client.Execute<List<string>>(request).Data;


// 답변 포맷은 "10_20 "

var TokenString = queryResult[0].Split(new Char[] { '_' }, StringSplitOptions.RemoveEmptyEntries);




2) 요청 데이터를 클래스 사용, 답변도 클래스로 얻기

var reqData = new REQ_DATA();

reqData.ID = IDText;

reqData.AreaID = AreaIDText);

var client = new RestSharp.RestClient(URLText);  //http://localhost:10101/Service 

string RestAPI = string.Format("{0}", APIText);  // RequestData


var request = new RestSharp.RestRequest(RestAPI, RestSharp.Method.POST);

request.RequestFormat = RestSharp.DataFormat.Json;

request.AddBody(reqData);


var queryResult = client.Execute(request);

var ResponData = Newtonsoft.Json.JsonConvert.DeserializeObject<ServiceLogic.RES_DATA>(queryResult.Content);

저작자 표시
신고
by 흥배 2013.11.25 08:00

. NET4에서는 워크 스테이션 GC용으로 백그라운드 GC를 가능하게 했다. 그 이후 힙 사이즈의 상한이 몇 기가바이트에서 수십 기가바이트인 컴퓨터를 자주 보게 되었다.

 

NET Framework 팀이 소유한 최적화된 병렬 가베지컬렉터라도 이런 큰 힙은 컬렉션에 몇 초가 걸리고 그 동안 응용 프로그램 스레드를 차단한다. 서버용 백그라운드 GC에서는 서버의 가베지컬렉터에  동시 실행(병렬 실행) 가베컬렉션의 지원을 도입하고 있다. 이에 따라 애플리케이션의 쓰루풋이 높은 상태를 유지하면서 장시간의 블록이 발생하는 가베 컬렉션을 최소한으로 억제할 수 있다.

 

서버 GC를 사용하는 경우 서버의 백그라운드 GC가 자동적으로 시작되도록 이 새 기능을 사용하는데 필요한 것은 아무것도 없다. 클라이언트 GC 및 서버 GC라도 백그라운드 GC의 고도의 특성은 같으며 다음과 같은 특성이 있다.

 

l  완전 GC(세대 2)만을 백그라우드로 할 수 있다.

l  백그라운드 GC는 최적화를 실시하지 않는다.

l  포어 그라운드 GC(세대 0/세대 1 GC)는 백그라운드 GC 안에서 할 수 있으며, 서버 GC는 전용 서버 GC 스레드에서 행해진다.

l  완전한 블로킹 GC도 전용의 서버 GC 스레드에서 행한다.

 

 

출처: http://msdn.microsoft.com/ko-kr/magazine/hh882452(en-us).aspx

저작자 표시
신고
by 흥배 2013.11.21 08:00

2013.9.12(미국 기준)Immutable Collection RC 버전이 나왔다. 입수는 NuGet으로 할 수 있다. 아직 정식 버전이 나오지 않아서 닷넷 프레임워크에는 들어가 있지 않지만 아마 다음 버전에서 들어가지 않을까 생각한다(VS 2014 정도)

http://blogs.msdn.com/b/dotnet/archive/2013/09/12/immutable-collections-are-now-rc.aspx

 

 

Immutable Collection는 말 그래도 변하지 않는 컬렉션이라고 생각하면 된다. 컬렉션을 처음 생성할 때 데이터를 입력하면 이후에 해당 데이터를 변경할 수 없다.

생성 후 데이터가 바뀌지 않으므로 의도하지 않은 데이터 변경을 막을 수 있고, 멀티스레드에 안전하며, 성능이 기존 컬렉션 보다 더 좋다.

올해 1월에 나온 관련 글을 보면 아래와 같은 성능 차이가 있다고 합니다.

http://www.infoq.com/resource/news/2013/01/Immutable-BCL/ja/resources/chart.png 

http://www.infoq.com/jp/news/2013/01/Immutable-BCL

 

기존 데이터를 꼭 바꾸고 싶다면 메모리 해제를 한 후 다시 생성하는 수 밖에 없다.

 

예제 코드

static void Test1()

{

var lst = new List<KeyValuePair<string, string>>();


lst.Add(new KeyValuePair<string, string>("key1", "data1"));


lst.Add(new KeyValuePair<string, string>("key2", "data2"));


lst.Add(new KeyValuePair<string, string>("key3", "data3"));


lst.Add(new KeyValuePair<string, string>("key4", "data4"));


lst.Add(new KeyValuePair<string, string>("key5", "data5"));



var im = System.Collections.Immutable.ImmutableDictionary.CreateRange<string, string>(lst);

im.Add("key6", "data6");

Console.WriteLine("요소 수:" + im.Count.ToString());


foreach (var data in im)

{

Console.WriteLine(string.Format("Key:{0}, Value:{1}", data.Key, data.Value));

}



lst.RemoveAt(0);

im = null;

im = System.Collections.Immutable.ImmutableDictionary.CreateRange<string, string>(lst);

Console.WriteLine("요소 수:" + im.Count.ToString());


foreach (var data in im)

{

Console.WriteLine(string.Format("Key:{0}, Value:{1}", data.Key, data.Value));

}

}


결과

 

 

 

Immutable Collection류가 Scala는 이미 있다고 한다.

 

저작자 표시
신고
by 흥배 2013.11.14 08:00

  • Method = "GET" 방식은 인자 타입은 string 이어야 하고 "/{id}" 방식이어야 한다.
    • Post 방식에서는 int 타입도 사용할 있다.

신고
by 흥배 2013.11.01 08:00

MongoDB 공식 C# Driver

  • CSharp Language Center

http://docs.mongodb.org/ecosystem/drivers/csharp/

  • CSharp Driver Tutorial

http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#csharp-driver-tutorial

  • 1.8.1 버전 API 문서

http://api.mongodb.org/csharp/1.8.1/

 

 

접속 시 인증

 

// on the connection string

var connectionString = "mongodb://user:pwd@localhost/?safe=true";

var server = MongoServer.Create(connectionString);

   

// in code

var settings = new MongoServerSettings() {

   DefaultCredentials = new MongoCredentials("user", "pwd"),

   SafeMode = SafeMode.True

};

   

var server = MongoServer.Create(settings);

 

 

서로 인증 정보가 다른 database를 사용할 때

 

var credentialsStore = new MongoCredentialsStore();

credentialsStore.AddCredentials(

      "hr", new MongoCredentials("user1", "pwd1"));

credentialsStore.AddCredentials(

      "inventory", new MongoCredentials("user2", "pwd2"));

   

var settings = new MongoServerSettings {

      CredentialsStore = credentialsStore,

      SafeMode = SafeMode.True

};

   

var server = MongoServer.Create(settings);

 

var credentials = new MongoCredentials("user", "pwd");

var database = server.GetDatabase("hr", credentials);

 

 

Admin으로 인증하기

 

var cs = "mongodb://user(admin):pwd@localhost/?safe=true";

var server = MongoServer.Create(cs);

   

var adminCredentials = new MongoCredentials("user", "pwd", true);

var settings = new MongoServerSettings {

      DefaultCredentials = adminCredentials,

      SafeMode = SafeMode.True

};

   

var server = MongoServer.Create(settings);

 

 

시간 사용

  • 입력
     

    // 201308011121 라는 문자열로 DateTime 객체를 만든다.

    // 중요: MongoDB는 UTC date 타임대를 사용하므로 System.Globalization.DateTimeStyles.AssumeUniversal 로 조정한다

    IFormatProvider culture = new System.Globalization.CultureInfo("", true);

    DateTime date = DateTime.ParseExact(date_time, "yyyyMMddHHmm", culture, System.Globalization.DateTimeStyles.AssumeUniversal);

       

       

    var login = BasicLogDB.GetCollection<BsonDocument>("Login");

       

    BsonDocument data = new BsonDocument()

    {

       {"ID", id},

       {"UniqNum", UniqueNumber},

       {"date", date}

    };

       

    login.Insert(data);

     

 

예제 Insert

  • 기본
     

    using MongoDB.Bson;

    using MongoDB.Driver;

    using MongoDB.Driver.Builders;

       

    MongoClient cli = new MongoClient(textBoxAddress.Text);

    MongoDatabase testdb = cli.GetServer().GetDatabase("test");

       

    MongoCollection<BsonDocument> Customers = testdb.GetCollection<BsonDocument>("Account");

                   

    try

    {

      for (int i = 0; i < Count; ++i)

      {

          BsonDocument data = new BsonDocument()

          {

              {"ID", string.Format("{0}{1}",textBoxPreFix.Text, (StartIndex+i).ToString())},

              {"PW", "1111"}

          };

       

          Customers.Insert(data);

      }

    }

    catch (Exception err)

    {

       MessageBox.Show(err.ToString());

    }

  • list 자료형을 값으로 사용 할 때
     

    List<int> userCount = new List<int>();

       

    BsonDocument insertData = new BsonDocument()

    {

        {"UserCount", new BsonArray(userCount)},

        {"date", datetime}

    };

       

    collection = dbLib.dataMiningLogDB.GetCollection<BsonDocument>("DailyHourLoginUserCount");

     

 

예제 Select

  • 하나만 반환
     

    public static bool Auth(string id, string pw)

    {

       MongoClient cli = new MongoClient("mongodb://17.20.30.21");

       MongoDatabase database = cli.GetServer().GetDatabase("test");

       var Accounts = database.GetCollection<BsonDocument>("Account");

       

       IMongoQuery query = Query.EQ("ID", id);

       var result = Accounts.Find(query).SingleOrDefault();

                   

       if (result == null)

       {

          return false;

       }

       

       if (result["PW"] != pw)

       {

          return false;

       }

       

      return true;

    }

  • 여러개 반환
     

    IMongoQuery query = Query.EQ("version", version);

    var results = ItemInfo.Find(query);

       

       

    List<ItemInfo> itemlist = new List<ItemInfo>();

       

    foreach (var iteminfo in results)

    {

       var item = new ItemInfo

       {

          version = version,

          ID = iteminfo["ID"].AsInt32,

          name = iteminfo["name"].AsString,

          Lv = iteminfo["Lv"].AsInt32

       };

       

       itemlist.Add(item);

    }

    //

    public class ItemInfo

    {

       public int version;

       public int ID;

       public string name;

       public int Lv;

    }

  • Where절 사용
     

    Query<Employee>.Where(d => d.EmployeeNumber == 1234)

    Query<Employee>.Where(d => d.Name == "John Doe")

    Query<Employee>.Where(d => d.EmployeeStatus != Status.Active)

    Query<Employee>.Where(d => d.Salary > 100000)

  • 복합 쿼리
     

    var query = Query.GTE("x", 1).LTE(3);

    // becomes

    var query = Query.And(Query.GTE("x", 1), Query.LTE("x", 3));

       

    var query = Query.Not("x").GT(1);

    // becomes

    var query = Query.Not(Query.GT("x", 1));

       

    var query = Query.Exists("x", true);

    // becomes

    var query = Query.Exists("x");

       

    var query = Query.Exists("x", false);

    // becomes

    var query = Query.NotExists("x");

  • 해당 collection 모든 데이터 가져오기
     

    List<AccountData> lstAccount = new List<AccountData>();

       

    MongoClient cli = new MongoClient("mongodb://172.20.60.221");

    MongoDatabase database = cli.GetServer().GetDatabase("WebTest");

    var Accounts = database.GetCollection<BsonDocument>("Account");

       

    // query문 없이 FindAll 사용, 해당 collection에 모든 데이터를 가져올 수 있다.           

    var result = Accounts.FindAll();

       

    foreach (var AccountInfo in result)

    {

        var Account = new AccountData

        {

            AccountID = AccountInfo["ID"].AsString,

            PassWord = AccountInfo["PW"].AsDouble

        };

    }

    return lstAccount;

     

 

예제 Update, Delete

 

// http://www.csharpstudy.com/Tips/Tips-mongodb.aspx

// Mongo DB를 위한 Connection String

string connString = "mongodb://localhost";

   

// MongoClient 클라이언트 객체 생성

MongoClient cli = new MongoClient(connString);

   

// testdb 라는 데이타베이스 가져오기

// 만약 해당 DB 없으면 Collection 생성시 새로 생성함

MongoDatabase testdb = cli.GetServer().GetDatabase("testdb");

   

// testdb 안에 Customers 라는 컬렉션(일종의 테이블)

// 가져오기. 만약 없으면 새로 생성.

var customers = testdb.GetCollection<Customer>("Customers");

   

// INSERT - 컬렉션 객체의 Insert() 메서드 호출

// Insert시 _id 라는 자동으로 ObjectID 생성

// 이 _id는 해당 다큐먼트는 나타는 키.

Customer cust1 = new Customer { Name = "Kim", Age = 30 };

customers.Insert(cust1);

ObjectId id = cust1.Id;

   

// SELECT - id로 검색

IMongoQuery query = Query.EQ("_id", id);

var result = customers.Find(query).SingleOrDefault();

if (result != null)

{

   Console.WriteLine(result.ToString());

}

   

// UPDATE

//    Save() = 전체 다큐먼트 갱신.

//    Update() = 부분 수정

cust1.Age = 31;

customers.Save(cust1);           

   

// DELETE

var qry = Query.EQ("_id", id);

customers.Remove(qry);

 

 

예제 FindAndModify

 

static Tuple<Int64, Int64> StoreUniqueNumber(int Count)

{

    MongoClient cli = new MongoClient(DB_ConnectString);

    MongoDatabase db = cli.GetServer().GetDatabase(BasicLogDatabaseName);

    var UniqueNumberCol = db.GetCollection<BsonDocument>("UniqueNumber");

   

    bool IsLoop = true;

   

    while (IsLoop)

    {

        var result = UniqueNumberCol.FindAll().SingleOrDefault();

   

        var firstNum = result["firts"].ToInt64();

        var lastNum = result["last"].ToInt64();

   

        var query = Query.And(

            Query.EQ("firts", firstNum),

            Query.EQ("last", lastNum)

        );

   

        var update = Update.Set("firts", lastNum + 1).Set("last", lastNum + Count);

   

        var newResult = UniqueNumberCol.FindAndModify(query, SortBy.Null, update, true);

           

        if (newResult.Ok)

        {

            var newFirstNum = result["firts"].ToInt64();

            var newLastNum = result["last"].ToInt64();

   

            return Tuple.Create(newFirstNum, newLastNum);

        }

    }

   

    return Tuple.Create((Int64)0, (Int64)0);

}

 

 

특정 필드 제외, 특정 필드만 쿼리하기

  • 특정 필드 제외
     

    MongoClient cli = new MongoClient(DB_ConnectString);

    MongoDatabase db = cli.GetServer().GetDatabase(BasicLogDatabaseName);

    var UniqueNumberCol = db.GetCollection<BsonDocument>("UniqueNumber");

       

    // _id 필드만 제외하고 모든 필드 데이터를 가져온다

    var newResult = UniqueNumberCol.FindAll().SetFields(Fields.Exclude("_id"));

  • 특정 필드만 가져오기
     

    MongoClient cli = new MongoClient(DB_ConnectString);

    MongoDatabase db = cli.GetServer().GetDatabase(BasicLogDatabaseName);

    var UniqueNumberCol = db.GetCollection<BsonDocument>("UniqueNumber");

       

    // money, level 필드 데이터만 가져온다

    var newResult = UniqueNumberCol.FindAll().SetFields(Fields.include("money", "level"));

     

 

날짜 쿼리

  • 특정날짜의 데이터 개수 알아내기
     

    // 어제 날짜

    dateTime = DateTime.Now.AddDays(-1);

    // 0시로 만들기 위해서

    DateTime dateTime2 = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day);

    var count = DBWork.DBDataMining.DailyNewUserStatistics(db, dateTime2);

       

    static public long DailyNewUserStatistics(MongoDBLib dbLib, DateTime dateTime)

    {

        var collection = dbLib.BasicLogDB.GetCollection<BsonDocument>("NewUser");

       

            //

        var query = Query.And(Query.GTE("date", dateTime.ToUniversalTime()), Query.LTE("date", dateTime.AddDays(1).ToUniversalTime()));

        var count = collection.Find(query).Count();

        return count;

    }

     

 

중복 제외

 

/*

{

    "_id" : ObjectId("5209d0507dbe2af068a42aec"),

    "ID" : "jacking75",

    "Uniq" : 5,

    "date" : ISODate("2013-08-12T06:21:04Z")

}

*/

   

DateTime datetime = MongoDBLib.StringToDateTime(yyyyMMddHHmm);

   

// 로그인한 총 수를 얻는다

var collection = dbLib.BasicLogDB.GetCollection<BsonDocument>("LoginUser");

var query = Query.And(Query.GTE("date", datetime), Query.LTE("date", datetime.AddDays(1)));

   

// ID 필드 값 중 중복된 것을 제외하고 개수를 구한다

var UniqueLoginUserCount = collection.Distinct("ID", query).Count();

 

 

 

LINQ

기본

  • 필요한 네임스페이스

MongoDB.Driver

MongoDB.Driver.Builders

MongoDB.Driver.Linq

MongoDB.Bson

MongoDB.Bson.Serialization.Attributes

 

 

[BsonIgnoreExtraElements]

public class AddressEntry

{

  [BsonId]

  public BsonObjectId _id { get; set;}

  public string Name { get; set; }

  public DateTime Birthday { get; set; }

  public List<Email> EmailAddrs { get; set; }

   

  [BsonIgnore]

   public string Detail { get; set; }  // 맵핑하지 않는다

}

   

[BsonIgnoreExtraElements]

public class Email

{

  public string DisplayName { get; set; }

  public string MailAddress { get; set; }

}

   

   

// MongoDB에 접속

MongoClient cli = new MongoClient();

// Database를 선택

MongoDatabase db = cli.GetServer().GetDatabase("TestDB");

// 컬렉션 취득

MongoCollection<AddressEntry> col = db.GetCollection<AddressEntry>("AddressBook");

 

public void Order_Where_Member_1()

{

    var orderCollection = GetCollection(); // 컬렉션 취득을 함수화

   

    var orders = orderCollection.AsQueryable().Where( x => x.MemberId == 1 ).ToList();

   

    orders.ForEach( x =>

            {

                WriteOrder( x );

                x.OrderDetails.ToList().ForEach( y => WriteOrderDetail( y ) );

            }

        );

}

 

 

&&

 

var query = from e in collection.AsQueryable<Employee>()

      where

         e.EmployeeStatus == Status.Active &&

         e.Salary > 100000

      select e;

   

// translates to:

{ EmployeeStatus : 1, Salary : { $gt : 100000 } }

 

 

문자열에서 일부 문자 찾기

 

var query = from e in collection.AsQueryable<Employee>()

      where e.Name.Contains("oh")

      select e;

   

// translates to:

{ nm: /oh/s }

   

   

var query = from e in collection.AsQueryable<Employee>()

      where e.Name.StartsWith("John")

      select e;

   

// translates to:

{ nm: /^John/s }

 

 

string Length, IsNullOrEmpty

 

var query = from e in collection.AsQueryable<Employee>()

      where e.Name.Length == 4

      select e;

   

// translates to:

{ nm: /^.{4}$/s }

   

   

var query = from e in collection.AsQueryable<Employee>()

      where string.IsNullOrEmpty(e.Name)

      select e;

   

// translates to:

{ $or : [ { nm: { $type : 10 } }, { nm: "" } ] }

 

 

string ToLower

 

var query = from e in collection.AsQueryable<Employee>()

      where e.Name.ToLower() == "john macadam"

      select e;

   

// translates to:

{ nm: /^john macadam$/is }

 

 

array element, Length

 

var query = from e in collection.AsQueryable<Employee>()

      where e.Skills[0] == "Java"

      select e;

   

// translates to:

{ "Skills.0" : "Java" }

   

   

var query = from e in collection.AsQueryable<Employee>()

       where e.Skills.Length == 3

       select e;

   

// translates to:

{ Skills : { $size : 3 } }

 

 

dotted names

 

var query = from e in collection.AsQueryable<Employee>()

      where e.Address.City == "Hoboken"

      select e;

   

// translates to:

{ "Address.City" : "Hoboken" }

 

 

$in

 

var states = new [] { "NJ", "NY", "PA" };

var query = from e in collection.AsQueryable<Employee>()

      where states.Contains(e.Address.State)

      select e;

   

// translates to:

{ "Address.State" : { $in : [ "NJ", "NY", "PA" ] } }

   

   

// alternative syntax using C#/.NET driver "In" method

var query = from e in collection.AsQueryable<Employee>()

     where e.Address.State.In(states)

     select e;

 

 

$in/$all with arrays

 

var desiredSkills = new [] { "Java", "C#" };

var query = from e in collection.AsQueryable<Employee>()

      where e.Skills.ContainsAny(desiredSkills)

      select e;

   

// translates to:

{ "Skills" : { $in : [ "Java", "C#" ] } }

   

   

var query = from e in collection.AsQueryable<Employee>()

     where e.Skills.ContainsAll(desiredSkills)

     select e;

   

// translates to:

{ "Skills" : { $all : [ "Java", "C#" ] } }

// note: ContainsAny and ContainsAll are defined by the C#/.NET

driver and are not part of standard LINQ

 

 

$elemMatch

 

var query = from e in collection.AsQueryable<Employee>()

   where

      e.Addresses.Any(a =>

         a.City == "Hoboken" &&

         a.State == "NJ")

   select e;

   

// translates to:

{ "Addresses" : { $elemMatch :

   { City : "Hoboken", State : "NJ" } } }

 

 

Mixing LINQ with MongoDB queries

 

var query = from e in collection.AsQueryable()

      where

         e.Salary > 50000 &&

         Query.NotExists("EmployeeStatus").Inject()

      select e;

   

// translates to:

{

   Salary : { $gt : 50000 },

   EmployeeStatus : { $exists : false }

}

 

 

 

참고 문서

  • What's new in the .NET Driver

  • CSharp Driver LINQ Tutorial

 

 

Error Code

신고
by 흥배 2013.10.21 08:00
| 1 ··· 3 4 5 6 7 8 |

티스토리 툴바