2011-07-18 2 views
4

이것이 바로 WCF 서비스를 사용하는 요점입니다. 그래서 저는 고객 측에서 일하기 시작합니다. 그리고 응용 프로그램이 실행 중일 때 예외가 발생했습니다 : 시간 초과. 그래서 나는 연결을 유지하는 방법에 대한 많은 예제가 있지만, 가장 좋은 방법은 채널을 만들고 사용하고 폐기하는 것입니다. 그리고 솔직히, 나는 그것을 좋아했다. 그래서, 지금 채널을 폐쇄하는 가장 좋은 방법에 대한 책을 읽은 그들 필요가있는 사람에게 도움이 될 수있는 두 개의 링크가 있습니다 : 첫 번째 링크에서C# WCF 클로징 채널 및 함수 사용 Func <T>

1. Clean up clients, the right way

2. Using Func

는, 이것은 예입니다 :

  IIdentityService _identitySvc; 
     ... 
     if (_identitySvc != null) 
     { 
      ((IClientChannel)_identitySvc).Close(); 
      ((IDisposable)_identitySvc).Dispose(); 
      _identitySvc = null; 
     } 

따라서 채널이 null이 아니면 닫히고 폐기하고 null을 할당하십시오. 하지만 약간의 질문이 있습니다. 이 예제에서는 채널에 .Close() 메서드가 있지만 내 경우에는 intellisense가 Close() 메서드를 표시하지 않습니다. 팩토리 객체에만 존재합니다. 그래서 나는 그것을 써야한다고 믿습니다. 그러나 인터페이스를 구현하는 클래스 나 클래스가있는 인터페이스에서 ??. 그리고,이 방법을 어떻게해야합니까 ??.

이제 다음 링크는 이전에 시도하지 않은 것입니다. Func<T>. 그리고 목표를 읽은 후에, 그것은 꽤 흥미 롭습니다. 그것은 lambdas가 채널을 생성하고, 사용하고, 닫고, 배치하는 기능을 만듭니다. 이 예제는 Using() 문과 같은 함수를 구현합니다. 정말 훌륭하고 훌륭한 개선점입니다. 그러나, 나는 약간의 도움이 필요하다. 솔직히 말해서, 나는 그 기능을 이해할 수 없다. 그래서 전문가로부터의 약간의 설명이 매우 유용 할 것이다. 이 기능입니다 :

TReturn UseService<TChannel, TReturn>(Func<TChannel, TReturn> code) 
{ 
    var chanFactory = GetCachedFactory<TChannel>(); 
    TChannel channel = chanFactory.CreateChannel(); 
    bool error = true; 
    try { 
     TReturn result = code(channel); 
     ((IClientChannel)channel).Close(); 
     error = false; 
     return result; 
    } 
    finally { 
     if (error) { 
      ((IClientChannel)channel).Abort(); 
     } 
    } 
} 

그리고 이것은 사용되는 방법입니다

int a = 1; 
int b = 2; 
int sum = UseService((ICalculator calc) => calc.Add(a, b)); 
Console.WriteLine(sum); 

그래, 난 정말, 정말 좋은 생각, 내가 프로젝트에서 사용하는 그것을 이해하고 싶습니다 나는 가지고있다.

항상 그렇듯이 많은 사람들에게 도움이되기를 바랍니다.

고맙습니다.

+0

사실 제 질문은 채널에 관한 것이 아니라 Generics에 관한 것입니다. 나는 아직도 이것에 붙어있다. 'UseService' 함수를 설명하는 데 도움이 될 것입니다! – BlackCath

답변

3

UseService 메서드는 요청을 보내기 위해 채널을 사용하는 대리자를 허용합니다. 대리자에는 매개 변수와 반환 값이 있습니다. WCF 서비스에 대한 호출을 대리자에 넣을 수 있습니다.

그리고 UseService에서 채널을 생성하고 채널을 위임자에게 전달합니다.이 대리인은 사용자가 제공해야합니다. 통화가 끝나면 채널이 닫힙니다.

+0

안녕하세요! 정말 답변을 주셔서 감사합니다. 그러나 나는 아직도 성명서에 갇혀있다 : 'var chanFactory = GetCachedFactory ();' 나는이 방법을 찾을 수 없으므로 스스로 작성해야하지만, 어떻게 ... ?? 내말은, 어떻게? 관심을 가져 주셔서 감사합니다. – BlackCath

2

프록시 객체는 당신의 계약보다 더 많은 구현 - 그것은 또한 프록시 수명

첫 번째 예제의 코드를 제어 할 수 있습니다 IClientChannel 구현 신뢰할 수 없습니다 - 채널이 이미 파열 된 경우는 누출 (예를 들어, 세션 기반 상호 작용에서 서비스가 중단되었습니다).

enum OnError 
{ 
    Throw, 
    DontThrow 
} 

static class ProxyExtensions 
{ 
    public static void CleanUp(this IClientChannel proxy, OnError errorBehavior) 
    { 
     try 
     { 
      proxy.Close(); 
     } 
     catch 
     { 
      proxy.Abort(); 

      if (errorBehavior == OnError.Throw) 
      { 
       throw; 
      } 
     } 
    } 
} 
: 당신은 여전히 ​​다음과 같이 또한 확장 방법으로이 작업을 수행 할 수 있습니다

클라이언트 측을 정리 프록시에 중단을 호출하는 오류의 경우, 두 번째 버전에서 볼 수 있듯이

그러나, 이것의 사용은

((IClientChannel)proxy).CleanUp(OnError.DontThrow); 

조금 복잡하지만 계약과 IClientChannel

모두를 확장 자신의 프록시 인터페이스를 만들 경우 당신이 더 우아 할 수 있습니다
interface IPingProxy : IPing, IClientChannel 
{ 

} 
1

제이슨의 대답에 대한 의견에 남겨진 질문에 대답하려면 GetCachedFactory의 간단한 예가 아래처럼 보일 수 있습니다. 이 예제는 "Contract"속성이 팩터 리가 생성 될 서비스의 ConfigurationName과 동일한 구성 파일에서 끝점을 찾아 생성 할 끝점을 찾습니다.

ChannelFactory<T> GetCachedFactory<T>() 
{ 
    var endPointName = EndPointNameLookUp<T>(); 
    return new ChannelFactory<T>(endPointName); 
} 

// Determines the name of the endpoint the factory will create by finding the endpoint in the config file which is the same as the type of the service the factory is to create 
string EndPointNameLookUp<T>() 
{ 
    var contractName = LookUpContractName<T>(); 
    foreach (ChannelEndpointElement serviceElement in ConfigFileEndPoints) 
    { 
     if (serviceElement.Contract == contractName) return serviceElement.Name; 
    } 
    return string.Empty; 
} 

// Retrieves the list of endpoints in the config file 
ChannelEndpointElementCollection ConfigFileEndPoints 
{ 
    get 
    { 
     return ServiceModelSectionGroup.GetSectionGroup(
      ConfigurationManager.OpenExeConfiguration(
       ConfigurationUserLevel.None)).Client.Endpoints; 
    } 
} 

// Retrieves the ConfigurationName of the service being created by the factory 
string LookUpContractName<T>() 
{ 
    var attributeNamedArguments = typeof (T).GetCustomAttributesData() 
     .Select(x => x.NamedArguments.SingleOrDefault(ConfigurationNameQuery)); 

    var contractName = attributeNamedArguments.Single(ConfigurationNameQuery).TypedValue.Value.ToString(); 
    return contractName; 
} 

Func<CustomAttributeNamedArgument, bool> ConfigurationNameQuery 
{ 
    get { return x => x.MemberInfo != null && x.MemberInfo.Name == "ConfigurationName"; } 
} 

더 나은 해결책은 IoC 컨테이너가 클라이언트 생성을 관리하도록하는 것입니다. 예를 들어, autofac을 사용하면 다음과 같이됩니다. 첫째로 당신은과 같이 서비스를 등록해야합니다 :

"WSHttpBinding_ICalculator는"config 파일에 엔드 포인트의 이름입니다
var builder = new ContainerBuilder(); 

builder.Register(c => new ChannelFactory<ICalculator>("WSHttpBinding_ICalculator")) 
    .SingleInstance(); 
builder.Register(c => c.Resolve<ChannelFactory<ICalculator>>().CreateChannel()) 
    .UseWcfSafeRelease(); 

container = builder.Build(); 

. 그런 다음 나중에 서비스를 사용할 수 있습니다.

using (var lifetime = container.BeginLifetimeScope()) 
{ 
    var calc = lifetime.Resolve<IContentService>(); 
    var sum = calc.Add(a, b); 
    Console.WriteLine(sum); 
}