WCFの本圓に透過的な䜿甚

やる気



デスクトップの䞖界にずっお、wcfは、ロヌカルネットワヌクずグロヌバルネットワヌクの䞡方で.netでクラむアントずサヌバヌの察話を敎理する最も䞀般的な方法です。 構成が柔軟で、䜿いやすく、透過的です。



少なくずもそうすべきです。 実際には、新しいサヌビスの远加は日垞的な䜜業です。 サヌバヌで構成を登録するこずを忘れないでください。クラむアントで同じこずを行い、プロキシクラスを䜜成たたは生成する必芁がありたす。 構成の維持は䞍䟿です。 サヌビスが倉曎された堎合は、プロキシクラスを倉曎する必芁がありたす。 たた、IoCコンテナヌに登録するこずを忘れないでください。 そしお、新しいサヌビス甚の新しいホストを远加したす。 たた、単玔な非同期性も必芁です。 それずは別に、すべおがシンプルですが、このリストをすでに3回远加した蚘事でも、䜕も芋逃しおいないかどうかはわかりたせん。



自動化する時間。 ゜リュヌションの䜜成からwcfサヌビスの呌び出したでの最も単玔なシナリオは、次のようになりたす。

  1. むンストヌルパッケヌゞRikrop.Core.Wcf.Unity
  2. ServiceContractずその実装を䜜成したす
  3. サヌバヌずクラむアントで、IoCに1行の登録を远加したす構成を線集する必芁はありたせん
  4. 2行からホストを䞊げる

    var assembly = Assembly.GetExecutingAssembly(); _serviceHostManager.StartServices(assembly);
          
          



  5. クラむアントで、IServiceExecutor <TService>を解決したす。 このラッパヌは、サヌビスのメ゜ッドを呌び出すのに圹立ち、チャネルでの䜜業を隠したす。
  6. 䜿甚できたす
     var articles = await _myServiceExecutor.Execute(service => service.GetArticles());
          
          







クむックスタヌト



クラむアント/サヌバヌアプリケヌションを䜜成したす。 クラむアントは、数倀のむンデックスをフィボナッチ数列からサヌバヌに枡し、サヌバヌは指定されたむンデックスを持぀数列をシヌケンスから返したす。 ロギングず゚ラヌ凊理は蚘事のコヌドから削陀されたしたが、 githubのコヌドでは党䜓的なアプロヌチを説明するより完党な䟋を瀺したす。

珟実に近いプロゞェクト構造







Server.Contractsには、wcfサヌビスのむンタヌフェむス、Server-それらの実装、およびhoster-wcfサヌビスを発生させるクラスの実装が含たれおいたす。 BL-サヌバヌロゞック。 ConsoleServiceHostは、コン゜ヌルアプリケヌションドメむンでサヌビスをホストしたす。 Client.Presentaionには、察応するクラむアント局が含たれおいたす。 この䟋では、サヌビス呌び出しコマンドず結果の凊理のみがありたす。 クラむアントは、以前のアセンブリを䜿甚しおナヌザヌ入力を凊理するコン゜ヌルアプリケヌションです。



実際には、nugetパッケヌゞは次のようにむンストヌルする必芁がありたす。



RikropWcfExample.Server.Contractsで、 wcfサヌビスの説明を远加したす 。



 using System.ServiceModel; using System.Threading.Tasks; namespace RikropWcfExample.Server.Contracts { [ServiceContract] public interface ICalculatorService { [OperationContract] Task<ulong> GetFibonacciNumber(int n); } }
      
      





CalculatorService.csの実装は、リク゚ストを枡し、ビゞネスロゞックレむダヌから結果を返したす。



 using RikropWcfExample.Server.BL; using RikropWcfExample.Server.Contracts; using System.Threading.Tasks; namespace RikropWcfExample.Server { public class CalculatorService : ICalculatorService { private readonly FibonacciCalculator _fibonacciCalculator; public CalculatorService(FibonacciCalculator.ICtor fibonacciCalculatorCtor) { _fibonacciCalculator = fibonacciCalculatorCtor.Create(); } public async Task<ulong> GetFibonacciNumber(int n) { return await _fibonacciCalculator.Calculate(n); } } }
      
      





今のずころ、泚意すべき点の1぀は、wcfサヌビスが非同期を蚘述するためにasync / awaitを䜿甚しおいるこずです。 それ以倖の堎合、特定のデザむンはありたせん。



それでは、登録に移りたしょう。 サヌバヌの最も単玔な構文は、バむンディングのタむプNetTcpにサヌビスに远加する必芁がある動䜜のリストを瀺したす。



 private static IUnityContainer RegisterWcfHosting(this IUnityContainer container, string serviceIp, int servicePort) { container .RegisterServerWcf( o => o.RegisterServiceConnection(reg => reg.NetTcp(serviceIp, servicePort)) .RegisterServiceHostFactory(reg => reg.WithBehaviors().AddDependencyInjectionBehavior()) ); return container; }
      
      





クラむアントの堎合、サヌビスのラッパヌ゚グれキュヌタヌのタむプServiceExecutor、バむンディングのラッパヌのタむプStandartはNetTcpを想定、実際にはサヌバヌアドレスが瀺されたす



 private static IUnityContainer RegisterWcf(this IUnityContainer container, string serviceIp, int servicePort) { container .RegisterClientWcf(o => o.RegisterServiceExecutor(reg => reg.Standard() .WithExceptionConverters() .AddFaultToBusinessConverter()) .RegisterChannelWrapperFactory(reg => reg.Standard()) .RegisterServiceConnection(reg => reg.NetTcp(serviceIp, servicePort))); return container; }
      
      





それだけです むンタヌフェむスごずに各サヌビスを登録する必芁はありたせん。プロキシを䜜成する必芁も、構成にwcfを登録する必芁もありたせん。これらの登録により、あたかもロヌカルコヌルのようにサヌビスの操䜜をすぐに開始できたす。

ただし、最初にサヌバヌでホストする必芁がありたす。 Rikrop.Core.WcfラむブラリにはすでにServiceHostManagerクラスが含たれおおり、これはすべおの䜜業を独自に実行したす。 各サヌビスを登録する必芁はありたせん。



 using Rikrop.Core.Wcf; using System.Reflection; namespace RikropWcfExample.Server { public class WcfHoster { private readonly ServiceHostManager _serviceHostManager; public WcfHoster(ServiceHostManager serviceHostManager) { _serviceHostManager = serviceHostManager; } public void Start() { var assembly = Assembly.GetExecutingAssembly(); _serviceHostManager.StartServices(assembly); } public void Stop() { _serviceHostManager.StopServices(); } } }
      
      





サヌバヌを起動したしょう



 public static void Main() { using (var serverContainer = new UnityContainer()) { serverContainer.RegisterServerDependencies(); var service = serverContainer.Resolve<WcfHoster>(); service.Start(); Console.WriteLine(" .    Enter."); Console.ReadLine(); service.Stop(); } }
      
      





クラむアントを実行したす 。



 static void Main() { using (var container = new UnityContainer()) { container.RegisterClientDependencies(); var calculateFibonacciCommandCtor = container.Resolve<CalculateFibonacciCommand.ICtor>(); int number; while (int.TryParse(GetUserInput(), out number)) { var command = calculateFibonacciCommandCtor.Create(); var result = command.Execute(number); Console.WriteLine("Fibonacci[{0}] = {1}", number, result); } } }
      
      





動䜜したす







埓来のアプロヌチおよび拡匵性ずの比范



提案された゜リュヌションは、非垞に倚くのむンフラストラクチャコヌドを必芁ずし、通垞のwcfの䜿甚に比べお利点がないように思われるかもしれたせん。 プロゞェクトの䜜業䞭に発生する兞型的な状況の䟋によっお違いを瀺すのが最も簡単です。



既存のwcfサヌビスぞの新しいメ゜ッドの远加、たたは既存のメ゜ッドの眲名の倉曎


Rikrop.Core.Wcf.Unity ラむブラリを䜿甚せずに
  • メ゜ッド定矩をServiceContractに远加したす。
  • wcfサヌビスのクラスに実装を远加したす。


これで、クラむアントで新しいメ゜ッドを呌び出すこずができたす。
  • メ゜ッド定矩をServiceContractに远加したす。
  • wcfサヌビスのクラスに実装を远加したす。
  • クラむアントでプロキシクラスを生成するか、クラむアントで新しいサヌビスを呌び出す手動実装を远加したすプロキシクラスを盎接䜿甚しない堎合は、䞡方のオプションがありたす。


これで、クラむアントで新しいサヌビスを呌び出すこずができたす。


既存のホストに新しいwcfサヌビスを远加する


Rikrop.Core.Wcf.Unity ラむブラリを䜿甚せずに
  • 新しいサヌビスのServiceContractを䜜成したす。
  • サヌビス契玄を実装したす。


これで、クラむアントで新しいサヌビスを呌び出すこずができたす。
  • 新しいサヌビスのServiceContractを䜜成したす。
  • サヌビス契玄を実装したす。
  • クラむアントでプロキシクラスを生成するか、クラむアントで新しいサヌビスを呌び出す手動実装を远加したすプロキシクラスを盎接䜿甚しない堎合は、䞡方のオプションがありたす。
  • 新しいサヌビスのServiceHostを初期化するwcfホストコヌドに远加したす。
  • 新しいサヌビスのプロキシクラスをクラむアントIoCコンテナヌに登録したす。
  • クラむアントではなくサヌバヌにサヌビス構成を远加したす。


これで、クラむアントで新しいサヌビスを呌び出すこずができたす。


すべおのwcfサヌビスの蚭定を倉曎する䟋ずしおバむンディングタむプを䜿甚
Rikrop.Core.Wcf.Unity ラむブラリを䜿甚せずに
  • サヌバヌ登録で、バむンディングタむプ*の行を倉曎したす。
  • クラむアント登録で、タむプバむンディングタむプ*の行を倉曎したす。


*パブリックの結果を参照カスタム <TServiceConnection>LifetimeManager lifetimeManager = null、params InjectionMember [] injectionMembersここで、TServiceConnectionIServiceConnection
  • app.configサヌバヌで、<bindings> *ブロックのすべおの゚ントリを倉曎したす。
  • クラむアントapp.configで、<bindings> *ブロックのすべおの゚ントリを倉曎したす。


*䜜業量はwcfサヌビスの数に比䟋したす。 それらが100個ある堎合、迅速なファむル眮換が機胜するこずのみを期埅できたす。


耇数のwcfサヌビスの蚭定の倉曎バむンディングタむプを䟋ずしお䜿甚


Rikrop.Core.Wcf.Unity ラむブラリを䜿甚せずに
  • サヌバヌで、新しいアドレスずバむンディングのタむプの登録を远加したす。
  • クラむアント登録で、異なるアドレスずバむンディングの皮類の新しい登録を远加したす。


  • サヌバヌのapp.configで、目的のwcfサヌビスの<bindings>ブロックの゚ントリを倉曎したす。
  • クラむアントapp.configで、目的のwcfサヌビスの<bindings>ブロックの゚ントリを倉曎したす。




最埌の2぀のポむントに぀いおいく぀かの蚀葉を蚀う䟡倀がありたす。 app.configが適切に構成されおいれば、倉曎を加えるのはずおも簡単です。 これは、アプリケヌションを再構築せずに実行できたす。 実際の開発では、wcf構造化構成は非垞にたれであり、これは反埩的な開発によるものです。 たた、初期蚭定が芁件を満たしおいる堎合、プログラマヌ以倖のナヌザヌも構成を頻繁に倉曎する必芁がありたす。 同時に、コンパむラが芋぀けられないようなタむプミスをするこずは簡単です。



拡匵性。 セッションの認可および動䜜の動䜜



登録時の動䜜の远加により、機胜の拡匵ず動䜜の倉曎が発生したす。 最も䞀般的なアプリケヌションは、wcfメッセヌゞのヘッダヌでセッション情報を送信する動䜜です。

機胜を瀺すために、前の䟋の拡匵コヌドを䜿甚しお別のブランチを䜜成したした。 暙準の動䜜蚭定では、開発者は承認方法を遞択するように求められたす-これはOperationContractです。これは、メッセヌゞヘッダヌにセッションのないナヌザヌが䜿甚できたす。 他のメ゜ッドの呌び出しは、入力されたヘッダヌでのみ可胜です。



サヌバヌでの登録は次のようになりたす。



 container .RegisterType<ISessionResolver<Session>, SessionResolver<Session>>() .RegisterServerWcf( o => o.RegisterServiceConnection(reg => reg.NetTcp(serviceIp, servicePort)) .RegisterServiceHostFactory(reg => reg.WithBehaviors() .AddErrorHandlersBehavior(eReg => eReg.AddBusinessErrorHandler().AddLoggingErrorHandler(NLogger.CreateEventLogTarget())) .AddDependencyInjectionBehavior() .AddServiceAuthorizationBehavior(sReg => sReg.WithStandardAuthorizationManager() .WithStandardSessionHeaderInfo("ExampleNamespace", "SessionId") .WithOperationContextSessionIdInitializer() .WithSessionAuthStrategy<Session>() .WithLoginMethod<ILoginService>(s => s.Login()) .WithOperationContextSessionIdResolver() .WithInMemorySessionRepository() .WithStandardSessionCopier()) ) );
      
      





System.ServiceModel.ServiceAuthorizationManagerの実装を远加するこずで認蚌方法を倉曎できたす。セッション識別子の初期化方法、認蚌の確認方法、ク゚リ実行のコンテキストからセッションを抜出する方法、サヌバヌにセッションを保存およびコピヌする方法を倉曎したす。 䞀般的な堎合、AuthorizationBehaviorの登録は次のようになりたす。



 .AddServiceAuthorizationBehavior(sReg => sReg.WithCustomAuthorizationManager<ServiceAuthorizationManagerImpl>() .WithCustomSessionHeaderInfo<ISessionHeaderInfoImpl>() .WithCustomSessionIdInitializer<ISessionIdInitializerImpl>() .WithCustomAuthStrategy<IAuthStrategyImpl>() .WithLoginMethod<ILoginService>(s => s.Login()) .WithCustomSessionIdResolver<ISessionIdResolverImpl>() .WithCustomSessionRepository<ISessionRepositoryImpl<MySessionImpl>>() .WithCustomSessionCopier<ISessionCopierImpl<MySessionImpl>>())
      
      





クラむアント登録も倉曎されたす。



 private static IUnityContainer RegisterWcf(this IUnityContainer container, string serviceIp, int servicePort) { container .RegisterType<ClientSession>(new ContainerControlledLifetimeManager()) .RegisterClientWcf(o => o .RegisterServiceExecutor(reg => reg.Standard() .WithExceptionConverters() .AddFaultToBusinessConverter()) .RegisterChannelWrapperFactory(reg => reg.Standard()) .RegisterServiceConnection(reg => reg .NetTcp(serviceIp, servicePort) .WithBehaviors() .AddSessionBehavior(sReg => sReg .WithStandardSessionHeaderInfo("ExampleNamespace", "SessionId") .WithCustomSessionIdResolver<ClientSession>(new ContainerControlledLifetimeManager()) .WithStandardMessageInspectorFactory<ILoginService>(service => service.Login())))); return container; }
      
      





結果







䜜業アルゎリズム


  1. クラむアントは、 wcfコントラクトで遞択されたメ゜ッドを介しお承認されたす。 認蚌に成功するず、サヌバヌはセッションを䜜成し、リポゞトリに保存し、それに関するデヌタをクラむアントに送信したす。



     var newSession = Session.Create(userId); _sessionRepository.Add(newSession); return new SessionDto { SessionId = newSession.SessionId, Username = "ExampleUserName" };
          
          



  2. クラむアントはセッションデヌタを受信しお​​保存したす。



     var clientSession = container.Resolve<ClientSession>(); var sessionDto = Task.Run(async () => await loginServiceExecutor.Execute(s => s.Login())).Result; clientSession.Session = sessionDto;
          
          



  3. サヌバヌには、呌び出しクラむアントに関するデヌタを受信する機胜がありたす。



     public async Task<ulong> GetFibonacciNumber(int n) { var session = _sessionResolver.GetSession(); _logger.LogInfo( string.Format("User with SessionId={0} and UserId={1} called CalculatorService.GetFibonacciNumber", session.SessionId, session.UserId)); return await _fibonacciCalculator.Calculate(n); }
          
          



  4. クラむアントには、蚱可䞭にサヌバヌから受信したデヌタを受信する機胜がありたす。



     _logger.LogInfo(string.Format("SessionId {0} with name {1} begin calculate Fibomacci", _clientSession.SessionId, _clientSession.Session.Username));
          
          





䞭身は䜕ですか



むンフラストラクチャのほずんどは、System.ServiceModel.dllラむブラリによっお提䟛されたす。 ただし、より詳现に怜蚎する必芁がある゜リュヌションがいく぀かありたす。



クラむアントずサヌバヌ間の察話の基瀎は、Rikrop.Core.WcfラむブラリにあるIServiceExecutorむンタヌフェむスの実装です。



 public interface IServiceExecutor<out TService> { Task Execute(Func<TService, Task> action); Task<TResult> Execute<TResult>(Func<TService, Task<TResult>> func); }
      
      





最も単玔な堎合、チャネルが開き、このチャネルのコンテキストでメ゜ッドが呌び出されたす。



 public async Task<TResult> Execute<TResult>(Func<TService, Task<TResult>> func) { using (var wrapper = _channelWrapperFactory.CreateWrapper()) { return await func(wrapper.Channel); } }
      
      





より耇雑な実装では、゚ラヌを倉換したり、プロパティの倉曎によっお凊理が完了したずきに通知するこずがありたす。 これらのアむデアは、IServiceExecutorのWPF実装で最も広く䜿甚されおいたす。ServiceExecutorFactoryを䜿甚するず、DataBindingを䜿甚しおUIに長い操䜜を通知したり、サヌバヌからの応答を埅機しながら任意の情報でポップアップを衚瀺したりできるwcfサヌビスのラッパヌを䜜成できたす。

実装を簡単にするために、䞻な圹割は、登録時のFluentむンタヌフェむスずラむブラリむンフラストラクチャの暙準実装によっお果たされたす。そのため、スタゞオのヒントを䜿甚するず、最も耇雑な構造でも初めお理解しやすくなりたす。







他のラむブラリも蚘事で間接的に蚀及されおいたす



たずめ



プロゞェクトにむンフラストラクチャをセットアップしたら、IServiceExexutorを介した察話のネットワヌクの性質を長い間忘れるこずができたす。 䜓系的なアプロヌチを䜿甚し、mvvmパタヌンを䜿甚したデスクトップアプリケヌションの構築、デヌタベヌスずの察話 、ロギング、およびその他の䞀般的なタスクにラむブラリを䜿甚するのが最適です。 ただし、銎染みのない、必ずしも銎染みのあるフレヌムワヌクを䜿甚したくない堎合でも、その基になるアむデアぞの応甚を芋぀けるこずができたす。 コンポヌネントの拡匵性、構成䞭の厳密なタむピング、すべおのレむダヌでの察話の透過性、むンフラストラクチャコヌドの最小化、およびむンフラストラクチャのメンテナンスに費やす時間は、蚈算機およびマルチナヌザヌ゚ンタヌプラむズシステムを䜜成する際に芚えおおくべき重芁な事項です。 ラむブラリを䜿甚する代わりに、プロゞェクトによっおラむブラリコヌドをダりンロヌドしお゜リュヌションに接続できたす。 これにより、デバッガヌの䞋で䜜業を孊習し、必芁に応じお独自の倉曎を加えるこずができたす。



ボヌナス



緎習に勝るものはありたせん。 Rikrop.Core.Wcfの䜿甚の開発ずサポヌトの間のどこかの段階で、かなり倧きなプロゞェクト玄300,000行のコヌドを翻蚳した経隓があるこずを孊びたした。 これは、.net 4.0のasync / awaitで苊しみ、セッションでの䜜業をカスタマむズし、蚭定から蚭定を抜出し、それらをcフォヌムに倉換するずいうかなり興味深い経隓です。 これが誰にずっおも興味深い堎合は、フレヌムワヌク党䜓をドラッグするこずなく、このラむブラリぞの移行の具䜓䟋を説明できたす。



たた、UIロックを介しおナヌザヌに通知するか、ServiceExecutorFactoryを介しお実装されたポップアップを䜿甚したwpfの゜リュヌションもありたす。 これはプラむベヌトな䟋であり、wcfよりもwpfに関連しおいたす。 ただし、これにより、ラむブラリの利点ず䜿甚の動機に関する詳现な情報が埗られたす。



All Articles