C# 에서 

어떤 변수를 기존에 만든 객체로 할당하게 되면 해당 변수는 그냥 기존에 만들어진 객체를 참조하기 때문에 기존에 있던 객체와 같은 객체가 된다.


예시를 보면 쉽게 이해할 수 있다.


Ex)

public class Car
{
    public string Name;

    public string Color;
}

Car car1 = new Car{Name=“supercar”, Color=“red”};

Car car2 = car1;

car1.Name = “my supercar”;

라고 car1의 Name 을 변경했을 때

Console.WriteLine(car2.Name);

을 출력하면 my supercar 가 출력된다.

즉, car1과 car2는 같은 메모리 어드레스를 참조하는 변수인 것이다.


그렇다면 car1과 같은 value를 같는 새로운 객체를 만들고 싶다면 어떻게 해야할까?

Copy 와 관련된 메서드인 닷넷 프레임워크에서 제공해주는 Object 클래스의 MemberwiseClone 메서드를 이용해서 ShallowCopy() 메서드와 DeepCopy() 메서드를 만들어서 사용하면된다.

두 메서드 의 차이는 클래스가 참조형 멤버를 가지고 있을 때 참조형 멤버 객체를 새로 생성하는냐 생성하지 않느냐에 차이다.


일단 참조형 멤버를 가진 클래스를 만들어보자.

public class CarBrand
{
    public string Name;

    public string PhoneNum;
}

public class Car
{
    public string Name;

    public string Color;

    public CarBrand Brand; 

    public Car ShallowCopy()
    {
        return (Car)this.MemberwiseClone();
    {

    public Car DeepCopy()
    {
        Car other = (Car) this.MemberwiseClone();
        other.Brand = new CarBrand(Brand);
        other.Name = String.Copy(Name);
        other.Color = String.Copy(Color);
        return other;
    }
}

Car 라는 객체는 CarBrand라는 객체를 멤버로가지고 있다.


이제 우리가 만든 ShallowCopy 와 DeepCopy()를 예제 코드를 사용해서 비교해보자.

EX)
CarBrand carBrand = new CarBrand{Name=“Audi”, PhoneNum=“01012341234”};
Car car1 = new Car {Name=“a4”, Color=“white”, Brand=carBrand};

Car car2 = car1.ShallowCopy();
car2.Name = “sonata”;
car2.Brand = new CarBrand{Name=“Hyundai”, PhoneNum=“01012341234”};

로 하고 값을 확인해보면

car1의 Brand로 현대로 바뀐 것을 확인할 수 있다. Name 은 여전히 a4이다.

즉, Brand 객체를 같이 참조하고 있는 것을 확인 할 수 있다.



포함하고 있는 참조 객체까지 새롭게 만들고 싶다면 위에서 만든 DeepCopy() 함수를 이용하면 된다.

EX)
CarBrand carBrand = new CarBrand{Name=“Audi”, PhoneNum=“01012341234”};
Car car1 = new Car {Name=“a4”, Color=“white”, Brand=carBrand};

Car car2 = car1.ShallowCopy();
car2.Name = “sonata”;
car2.Brand = new CarBrand{Name=“Hyundai”, PhoneNum=“01012341234”};

로 하고 값을 확인해보면

car1의 Brand는 여전히 audi이고 car2의 Brand만 현대로 바뀐 것을 확인할 수 있다. 

즉, car2 에서 Brand 객체를 새롭게 만들어서 사용한것 을 확인할 수 있다.



위에서 살펴본 것 처럼 기본적으로 만들어진 객체를 이용해서 변수에 할당하게 되면 새로운 객체가 아니라 기존에 객체를 참조하게 되므로 shallowcopy나 deepcopy 같은 함수를 만들어서 사용해야 한다. 

shallowcopy와 deepcopy의 차이는 가지고 있는 멤버 중에 참조객체를 
새로 만드느냐 아니면 기존의 것을 같이 참조하냐의 차이다.







C# 과 ASP.NET CORE를 공부하면서 가장 힘든 점 중 하나는 턱없는 한글 문서입니다.

그래서 비동기 함수에서 ConfigureAwait(false)를 왜 사용하는지에 대한 좋은 글이 있어서 번역하게 됐습니다.

저자의 동의를 얻어서 번역한 자료입니다!



다른 분들에게 도움이 되었으면 좋겠네요 :)



원본:

저자: Juan


본문:

.NET4.5  부터 async/await 를 도입하면서 asynchronous code를 작성하기가 많이 쉬워졌다.

Async/Await 키워드들은 synchronous 코드 와 비슷하고 컴파일러가 asynchrous 프로그래밍에서 처리하기 가장 어려운 부분을 처리해주면서코드 가독성과 프로그래머의 생산성을 향상시켰다.

Async 코드를 만들기가 얼마나 쉬운지 알아보기 위해 컨텐츠를 string으로 반환하는 curl 코드를 example로 만들어보자.

public async Task<string> DoCurlAsync()
{
    using(var httpClient = new HttpClient())
    using(var httpresponse = await httpclient.GetAsync(“https://www.bynder.com”))
    {
        return await httpResponse.Content.ReadAsStringAsync();
    }
}


우리는 bynder의 content를 가져오는 동안 다른 쓰레드를 호출하는 것을 막지 않는 비동기 호출을 했다.

이론적으로(상상 속에서) 사람들은 항상 우리가 만든 DoCurlAsync 함수를 아래와 같이 사용할 것 이다.

var bynderContnets = await DoCurlAsync();


하지만, 프로그래밍 세계는 이상과 거리가 멀다. 몇몇 사람들은 다음과 같이 사용할 수 있다.

var bynderContents = DoCurlAsync().Result

이렇게 코드를 작성하면 동기 방식으로 코드가 수행되기 때문에 curl 함수가 종료될 때 까지 다른 쓰레드를 호출 하는것을 막는다. 만약 콘솔 어플리케이션을 실행시키는 중이라면, 우리의 코드는 대부분 예상대로 실행될 것이다.

그러나 아래와같이, 만약 UI Application에서 수행이 되어진다면, 예를 들어서 button을 클릭했을 때 수행이 되는거라면

public void OnButtonClicked(object sender, RouteEventArgs e)
{
    var bynderContents = DoCurlAsync().Result;
}


이 어플리케이션은 동작하지 않을 것이고 deadlock 상태가 되어 버립니다. 물론, 우리가 만든 함수를 사용하는 사람들은 우리의 함수가 application을 응답하지 못하게 만들었다고 불평할 것 입니다.

이와 같은 상황의 문제를 해결하기 위해서 우리는 함수를 아래와 같이 다시 작성합니다.

public async Task<string> DoCurlAsync()
{
    using(var httpClient = new HttpClient())
    using(var httpresponse = await httpclient.GetAsync(“https://www.bynder.com”).ConfigureAwait(false))
    {
        return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
    }


사실 , 첫번째의 비동기 구문에 ConfigureAwait(false) 를 붙이는 것으로 이 문제는 충분히 해결할 수 있습니다.

public async Task<string> DoCurlAsync()
{
    using(var httpClient = new HttpClient())
    using(var httpresponse = await httpclient.GetAsync(“https://www.bynder.com”).ConfigureAwait(false))
    {
        return await httpResponse.Content.ReadAsStringAsync();
    }

결론적으로, 항상 ConfigureAwait(false)를 사용하는 것은 우리가 원치 않은 상황을 막기위한 good practice 입니다.




이제, 우리는 UI 어플리케이션(대부분의 콘솔 어플리케이션이 아닌)에서 dead lock이 발생하는지 분석하고 왜 ConfigureAwait(false)과 이 문제를 해결하는지 분석해볼 것 입니다.


먼저, 우리는 UI 어플리케이션 어떻게 동작하는지 이해하는 작업이 필요합니다.

  • UI 응답을 위한 UI 스레드라는 하나의 스레드가 있습니다. UI는 오직 이 쓰레드를 호출해야만 업데이트 할 수 있습니다. 그래서 만약 에 쓰레드 호출이 막혀 있다면 어플리케이션은 응답하지 않은 것으로 보입니다.
  • UI 쓰레드는 수행할 notification과 action을 받을 message queue를 가지고 있습니다. Win32에서는 아래와 같이 표현할 수 있습니다.

    • while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
      {
          // No errors are handled, for simplicity purposes.
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }

  • UI 쓰레드는 기본적으로 SynchronizationContext를 가지고 있습니다.
    • SynchronizationContext
      다양한 동기화 모델에서 동기화 컨텍스트를 전파하는 기본 기능을 제공해주는 class

만약에 SynchronizationContext가 있다면 (UI 스레드에서 코드가 실행된다면) await 이후의 코드들은 원래의 thread context에서 수행될 것 라고 기본적으로 예상할 수 있습니다.

만약, 아래 예제와 같이 우리가 UI 컴포넌트를 UI 스레드가 아닌 다른 스레드에서 수정하려고 한다면 System.InvalidOperationException이 발생할 것입니다.


public void OnButtonClicked(object sender, RouteEventArgs e)
{
    var bynderContents = await DoCurlAsync();
    myTextBlock.text = bynderContents;
}


다시 우리의 예제 코드를 돌아와보면 , 우리의 async DoCurlAsync 호출은 개념적으로 아래 코드와 같다.

var currentContext = SynchronizationContext.Current;
var httpResponseTask = httpClient.GetAsync("https://www.bynder.com");
httpResponseTask.ContinueWith(delegate
{
    if (currentContext == null)
    {
        return await httpResonse.Content.ReadAsStringAsync();
    }   
    else
    {
        currentContext.Post(delegate {
            await httpResonse.Content.ReadAsStringAsync();
        }, null);
     }
}, TaskScheduler.Current);


NOTE: 이 snippet은 await 구문을 사용했을 때 발생하는 것과 비슷한 버젼의 코드다. 이것은 resources들은 사용하고 closing하는 부분은 다루지 않았다.

Post 요청은 UI 스레드 메시지 펌프에 처리 될 메시지들을 보내고, 그래서 DoCurlAsync를 끝내기 위해서는 UI Thread가  await httpResonse.Content.ReadAsStringAsync();를 실행하는 것이 필수적이다.

그러나 , 아래와 같은 시나리오에서는

public void OnButtonClicked(object sender, RoutedEventArgs e)
{
    var bynderContents = DoCurlAsync().Result;
}

UI 스레드가 막혀 있으므로 instruction을 수행할 수없다. DoCurlAsync는 결코 끝나지 않으므로 dead lock상태에 빠진다. ConfigureAwait(false)는 await 후의 코드를 호출자 컨텍스트에서 수행할 필요가 없게 해준다. 그러므로 어떠한 deadlock도 피할 수 있다. 



이 글은 왜 비동기 함수를 호출 할때 ConfigureAwait(fasle)를 호출하는 것이 

Best Practice 인지 설명해줍니다.


그 이유가 궁금하셨던 분들이 계셨다면 도움이 되면 좋겠습니다.


감사합니다.








ASP.NET Core Cosmos db API

GITHUB 코드:


// program class 안에 초기 configuration

private const string EndpointUri = “http://localhost:8001”;
private const string PrimaryKey = “”
private DocumentClient client


# client 객체 생성
this.client = new DocumentClient(new Uri(EndpointUri, PrimaryKey)

# Database create
await this.client.CreateDatabaseIfNotExistsAsync( new Database { Id = “FamilyDB”} );

# Database delete
await this.DeleteDatabaseAsync(UriFacotory.CreateDatabaseUri(“”));

# Collection create
await this.client.CreateDocumentCollectionIfNotExistsAync(UriFactory.CreateDatabaseuri(“FamilyDB”),  new DocumentCollection { Id = “FamilyCollection” })

# creata document in Collection
 await this.client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(databaseName, collectionName), family);

# read document
 await this.client.ReadDocumentAsync<Family>(UriFactory.CreateDocumentUri("FamilyDB", "FamilyCollection", andersenFamily.Id));

# update document
await this.client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(databaseName, collectionName, familyName, updatedFamily);

# delete document
await this.client.DeleteDocumentAsync(UrtiFactory.CreateDocumentUri(databaseName, collectionName, docmentName));

# Linq query

FeedOptions
FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
IQueryabke<Family> familyQuery = this.client.CreateDocumentQuery( UriFactory.CreateDocumentCollectionUri(databasName, collectionName), queryOptions).Where(f => f.LastName == “:)
foreach (Family family in familyQuery)
{
Console.WrtieLine(family)
}

IQueryable<Family> familyQueryInSql = this.client.CreateDocumentQuery<Family>( UriFactory.CreateDocumentCollectionUri(databaseName, CollectionName), “SELECT * FROM family WHERE Family.LastName=‘Andresen”, queryOptions);



참고자료:

MSDN 자료










URL rewrite (url 재작성) VS URL redirection


Url redirection 작업은 client 작업으로 서버를 왕복해야한다.



Url rewrite 작업은 서번 내에서 특정 url request 왔을 url 다시 작성해서 서버에 요청함으로써 client에서 서버를 왕복할 필요가 없다.


URL 재작성 미들웨어를 사용해야 하는 경우

Windows Server에서 IIS URL 재작성 모듈 사용할 없거나, Apache Server에서 Apache mod_rewrite 모듈 사용할 없거나, Nginx에서 URL 재작성 사용할 없거나, 또는 응용 프로그램이 HTTP.sys 서버(기존의 WebListener)에서 호스팅 되는 경우에 URL 재작성 미들웨어를 사용해야 합니다. IIS, Apache 또는 Nginx에서 서버 기반의 URL 재작성 기술을 사용하는 가장 이유는 미들웨어가 이런 모듈들의 모든 기능을 지원하지 않고 대부분 미들웨어의 성능이 모듈의 성능을 따라가지 못하기 때문입니다. 그러나 IIS 재작성 모듈의 IsFile  IsDirectory 제약 조건 같이 ASP.NET Core 프로젝트에서 동작하지 않는 일부 서버 모듈 기능도 존재합니다. 바로 이런 시나리오에서 대신 미들웨어를 사용합니다.

=> 최대한 단에서 URL 재작성을 하는 좋다. (성능상)

이용
    using (StreamReader apacheModRewriteStreamReader =
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader =
        File.OpenText("IISUrlRewrite.xml"))
    {
// 처리하고자 하는 순서대로 여러 규칙을 연결하면 됩니다
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXMLRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));
        app.UseRewriter(options);
    }

RedirectOption 인스턴스를 만들어주고 UserRewriter 인자로 넣어준다.


Redirect
 .AddRedirect("redirect-rule/(.*)", "redirected/$1") 사용하면 redirect 사용할 있고, 기본적으로 302 status code 응답한다.


301 vs 302 , 검색 엔진 최적화 관점

301 라디이렉트는 무엇인가요?
301 혹은 영구이동(Permanently Moved) 해당 URL 영구적으로 새로운 URL 변경되었음을 나타냅니다. 검색엔진 크롤러는 301 요청을 만나면 컨텐트가 완전히 새로운 URL 영원히 이동했다고 판단합니다.

301 언제써야 할까요?
예를들어 웹사이트의 도메인을 변경했거나 새로운 URL 구조로 개편했을 사용할 있습니다. 검색엔진은 301 요청을 만나면 컨텐트가 새로운 URL 영원히 이동했다고 판단합니다. 따라서 검색엔진은 과거 URL 페이지랭킹과 평가점수를 새로운 URL 이동시킵니다.

302 리다이렉트는 무엇인가요?
302 리다이렉트의 의미는 요청한 리소스가 임시적으로 새로운 URL 이동했음(Temporarily Moved) 나타냅니다. 따라서 검색엔진은 페이지랭킹이나 링크에 대한 점수를 새로운 URL 옮기지 않으며 기존 URL 그대로 유지합니다. 검색엔진이 기존 URL 보유한 페이지랭킹 점수는 그대로 유지하도록 하면서 컨텐트만 새로운 URL에서 조회하도록 해야할 유용합니다

302 언제써야 할까요?
예를들어 쇼핑몰과 같은 전자상거래 사이트를 운영한다고 생각해봅시다. 인기리에 팔리는 제품이 일시적으로 재고가 떨어지거나 혹은 특정한 계절이나 기간에만 한정적으로 파는 제품이였다고 가정해봅시다. 해당 제품이 보유한 사이트랭크를 유지하면서 사용자에게 일시적으로 제품이 품절됐음을 알리려면 어떻게 해야할까요? 이럴 301 사용하거나 혹은 페이지의 컨텐트를 변경하게 되면 사이트랭킹 점수가 달라지게 것입니다. 대신 302 사용하게 되면 검색엔진은 일시적으로 해당 URL 사이트랭크는 보존하게 되며 사용자는 새로운 URL 컨텐트를 보게 됩니다.

출처http://nsinc.tistory.com/168 [NakedStrength Inc.]


AddRedirectToHttps
AddRedirectToHttpsPermanent