WCF рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдВрджреЗрд╢ рдЖрдзрд╛рд░рд┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛ SOAP рдХрд╛ рдирд┐рд░реНрдорд╛рдг

рдореИрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ WCF рдХреЛ рдПрдХ рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╕рдВрдж рдХрд░рддрд╛ рд╣реВрдВ рдЬреЛ рд╕рдВрдЪрд╛рд░ рдкрд░рдд рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЛ рд╕рд░рд▓ рдмрдирд╛рддрд╛ рд╣реИред рд▓реЗрдХрд┐рди WCF рдХреА рдбрд┐рдЬрд╝рд╛рдЗрди рд╢реИрд▓реА рдореЗрд░реЗ рдЕрдиреБрд░реВрдк рдирд╣реАрдВ рд╣реИред рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдкреНрд░рддреНрдпреЗрдХ рдбреАрдЯреАрдУ рдХреЗ рд▓рд┐рдП рдПрдХ рдирдпрд╛ рддрд░реАрдХрд╛ рдмрдирд╛рдирд╛ рдПрдХ рдЕрдЪреНрдЫрд╛ рд╕рдорд╛рдзрд╛рди рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреАред



WCF рдХреА рдХреБрдЫ рд╕реАрдорд╛рдПрдБ рд╣реИрдВ:



рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ RPC (рджреВрд░рд╕реНрде рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЙрд▓) рд╢реИрд▓реА рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕рдмрд╕реЗ рдЙрдкрдпреБрдХреНрдд рдирд╣реАрдВ рд╣реИред рд╕реЗрд╡рд╛ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП, рдФрд░ рдЙрд╕ рдкрд░ рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХрд╛ рдкреНрд░рднрд╛рд╡ рдиреНрдпреВрдирддрдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рджреВрд░рд╕реНрде рдПрдкреАрдЖрдИ рдХреЛ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХреЛ рдкреВрд░рд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП:



рдПрдХ рд╕рдВрджреЗрд╢-рдЖрдзрд╛рд░рд┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛ рдПрдХ рд╕рдВрджреЗрд╢ рдЕрдореВрд░реНрдд рдЬреЛрдбрд╝рдХрд░ WCF рдХреА рдЕрдзрд┐рдХрд╛рдВрд╢ рд╕реАрдорд╛рдУрдВ рдХреЛ рдкрд╛рд░ рдХрд░ рдЬрд╛рддреА рд╣реИред

рдЗрд╕ рд▓реЗрдЦ рдХреЛ рдкрдврд╝рдиреЗ рдХреЗ рдмрд╛рдж, рдЖрдк рд╕реАрдЦреЗрдВрдЧреЗ рдХрд┐ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп SOAP рд╕рдВрджреЗрд╢-рдЖрдзрд╛рд░рд┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХреИрд╕реЗ рдХрд░реЗрдВ (рдФрд░ рд╣рд░ рд╕рдордп рдирдП рдмрдирд╛рдирд╛ рдмрдВрдж рдХрд░реЗрдВ)ред



рд╡реЗрдм рд╕реЗрд╡рд╛ рдбрд┐рдЬрд╛рдЗрди



рдЖрдЗрдП рдЖрд░рдкреАрд╕реА-рд╢реИрд▓реА рдХреЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг, рд╕рд╛рде рд╣реА рд╕рдВрджреЗрд╢ рдЖрдзрд╛рд░рд┐рдд рджреГрд╖реНрдЯрд┐рдХреЛрдг рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВред



рдЖрд░рдкреАрд╕реА рдбрд┐рдЬрд╛рдЗрди



рдЖрд░рдкреАрд╕реА рд╢реИрд▓реА рдХрд╛ рдореБрдЦреНрдп рд╡рд┐рдЪрд╛рд░ рдЧреНрд░рд╛рд╣рдХреЛрдВ рдХреЛ рд╕реНрдерд╛рдиреАрдп рд╡рд╕реНрддреБрдУрдВ рдХреЗ рд░реВрдк рдореЗрдВ рджреВрд░рд╕реНрде рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХрд╛ рдЕрд╡рд╕рд░ рджреЗрдирд╛ рд╣реИред WCF рдореЗрдВ, рдПрдХ ServiceContract рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рд╛рдЗрдб рдкрд░ рдЙрдкрд▓рдмреНрдз рд╕рдВрдЪрд╛рд▓рди рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП:

[ServiceContract] public interface IRpcService { [OperationContract] void RegisterClient(Client client); [OperationContract] Client GetClientByName(string clientName); [OperationContract] List<Client> GetAllClients(); }
      
      





рд╕реЗрд╡рд╛ рдЕрдиреБрдмрдВрдз рдмрд╣реБрдд рд╕рд░рд▓ рд╣реИ рдФрд░ рдЗрд╕рдореЗрдВ рддреАрди рдСрдкрд░реЗрд╢рди рд╢рд╛рдорд┐рд▓ рд╣реИрдВред рд╣рдореЗрдВ рд╕реЗрд╡рд╛ рдЕрдиреБрдмрдВрдз рдореЗрдВ рдХрд┐рд╕реА рднреА рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рдмрд╛рдж рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдмрджрд▓рдирд╛ рд╣реЛрдЧрд╛ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдХрд┐рд╕реА рдСрдкрд░реЗрд╢рди рдХреЛ рдЬреЛрдбрд╝рдирд╛ рдпрд╛ рдирд┐рдХрд╛рд▓рдирд╛, рдСрдкрд░реЗрд╢рди рдХреЗ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдХреЛ рдмрджрд▓рдирд╛)ред рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ 10 рд╕реЗ рдЕрдзрд┐рдХ рдСрдкрд░реЗрд╢рди рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдПрдХ рд╕реЗрд╡рд╛ рдХреЛ рдмрдирд╛рдП рд░рдЦрдирд╛ рдФрд░ рдЧреНрд░рд╛рд╣рдХреЛрдВ рдХреЛ рдмрд╣реБрдд рд╕рдордп рд▓рдЧрддрд╛ рд╣реИред



рд╕рдВрджреЗрд╢ рдЖрдзрд╛рд░рд┐рдд рдбрд┐рдЬрд╛рдЗрди



рд╕рдВрджреЗрд╢-рдЖрдзрд╛рд░рд┐рдд рджреГрд╖реНрдЯрд┐рдХреЛрдг рдбреЗрдЯрд╛ рдЯреНрд░рд╛рдВрд╕рдлрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдФрд░ рдЧреЗрдЯрд╡реЗ рдкреИрдЯрд░реНрди рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИред рдбреАрдЯреАрдУ рдореЗрдВ рд╕рдВрдЪрд╛рд░ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╕рднреА рдбреЗрдЯрд╛ рд╣реИрдВ, рдФрд░ рдЧреЗрдЯрд╡реЗ рд╕рдВрдЪрд╛рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕реЗ рдЖрд╡реЗрджрди рдХреЛ рдЕрд▓рдЧ рдХрд░рддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП рдПрдХ рд╕рдВрджреЗрд╢-рдЖрдзрд╛рд░рд┐рдд рд╕реЗрд╡рд╛ рдПрдХ рдЕрдиреБрд░реЛрдз рд╕рдВрджреЗрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреА рд╣реИ рдФрд░ рдПрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕рдВрджреЗрд╢ рджреЗрддреА рд╣реИред рдЖрдЗрдП рдЕрдореЗрдЬрд╝реЕрди рдПрдкреАрдЖрдИ рд╕реЗ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджреЗрдЦреЗрдВред



рдЕрдиреБрд░реЛрдз рдЙрджрд╛рд╣рд░рдг:

 https://ec2.amazonaws.com/?Action=AllocateAddress Domain=vpc &AUTHPARAMS
      
      





рдЙрддреНрддрд░ рдЙрджрд╛рд╣рд░рдг:

 <AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/2013-02-01/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <publicIp>198.51.100.1</publicIp> <domain>vpc</domain> <allocationId>eipalloc-5723d13e</allocationId> </AllocateAddressResponse>
      
      





рдЗрд╕ рдкреНрд░рдХрд╛рд░, рд╕реЗрд╡рд╛ рдЕрдиреБрдмрдВрдз рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрдирд╛ рдЪрд╛рд╣рд┐рдП:

 public interface IMessageBasedService { Response Execute(Request request); }
      
      



рдЬрд╣рд╛рдВ Request



рдФрд░ Response



рдХреЛрдИ рднреА рдбреАрдЯреАрдУ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдпрд╛рдиреА рдПрдХ рд╡рд┐рдзрд┐ рд╕реЗ рд╣рдо рдХрд┐рд╕реА рднреА рдЖрд░рдкреАрд╕реА рд╕реЗрд╡рд╛ рдЕрдиреБрдмрдВрдз рдХреЛ рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдбрдмреНрд▓реНрдпреВрд╕реАрдПрдл рдЖрд░рдкреАрд╕реА рд╢реИрд▓реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред



рд╕рдВрджреЗрд╢ рдЖрдзрд╛рд░рд┐рдд рд╢реИрд▓реА



рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЬрд╛рдирддреЗ рд╣реИрдВ, рдПрдХ рд╕рдВрджреЗрд╢-рдЖрдзрд╛рд░рд┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛ рдХреЗ рд▓рд┐рдП, рд╣рдо рдХрд┐рд╕реА рднреА рдбреАрдЯреАрдУ рдХреЛ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП Request



рдФрд░ Response



рд╡рд╕реНрддреБрдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди WCF рдЗрд╕ рдбрд┐рдЬрд╛рдЗрди рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред WCF рдореЗрдВ рд╕рднреА рд╕рдВрдЪрд╛рд░ рдЗрдВрдЯрд░реНрди рд╕рдВрджреЗрд╢ рд╡рд░реНрдЧ рдХреЗ рдЙрдкрдпреЛрдЧ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИрдВред рдЕрд░реНрдерд╛рддреН, WCF рдХрд┐рд╕реА рднреА DTO рдХреЛ рдПрдХ Message



рдЙрджрд╛рд╣рд░рдг рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рд╕рд░реНрд╡рд░ рдкрд░ рдПрдХ Message



рднреЗрдЬрддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рд╣рдореЗрдВ Request



рдФрд░ Response



рд╡рд╕реНрддреБрдУрдВ рдХреЗ рд▓рд┐рдП Message



рд╡рд░реНрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕реЗрд╡рд╛ рдЕрдиреБрдмрдВрдз Response



рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рд╕рд╛рде рдФрд░ рдЙрд╕рдХреЗ рдмрд┐рдирд╛ рд╕рдВрдЪрд╛рд░ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИред

 [ServiceContract] public interface ISoapService { [OperationContract(Action = ServiceMetadata.Action.ProcessOneWay)] void ProcessOneWay(Message message); [OperationContract(Action = ServiceMetadata.Action.Process, ReplyAction = ServiceMetadata.Action.ProcessResponse)] Message Process(Message message); }
      
      



ISoapService



рд╣рдореЗрдВ рдХрд┐рд╕реА рднреА рдбреЗрдЯрд╛ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИред рд╣рдо рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдмрдирд╛рдирд╛, рд╣рдЯрд╛рдирд╛ рдФрд░ рдЙрд╕ рдкрд░ рддрд░реАрдХреЛрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдореЗрд░реЗ рд▓рд┐рдП, рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рд╡рд┐рдХрд▓реНрдк рдСрдмреНрдЬреЗрдХреНрдЯ рдкрд░ CRUD рдСрдкрд░реЗрд╢рди рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдХрд┐рд╕реА рднреА рдСрдкрд░реЗрд╢рди рдХреЛ рд▓рд╛рдЧреВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдПрдХ SoapServiceClient



рдмрдирд╛рдПрдВ рдЬреЛ рдХрд┐рд╕реА рднреА рдбреАрдЯреАрдУ рдХреЛ рднреЗрдЬ рдФрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред



рд╕рд╛рдмреБрди рд╕реЗрд╡рд╛ рдЧреНрд░рд╛рд╣рдХ



SoapServiceClient



рдХрд┐рд╕реА рднреА рдбреАрдЯреАрдУ рд╕реЗ Message



рдмрдирд╛рдиреЗ рдХрд╛ рддрд░реАрдХрд╛ рджрд┐рдЦрд╛рдПрдЧрд╛ред SoapServiceClient



рдПрдХ рдЖрд╡рд░рдг рд╣реИ рдЬреЛ рдХрд┐рд╕реА рднреА рдбреАрдЯреАрдУ рдХреЛ Message



рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рд╕реЗрд╡рд╛ рдореЗрдВ рднреЗрдЬрддрд╛ рд╣реИред рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рднреЗрдЬреЗ рдЧрдП рд╕рдВрджреЗрд╢ рдореЗрдВ рдирд┐рдореНрди рдбреЗрдЯрд╛ рд╣реИ:

рд╣рдорд╛рд░рд╛ рд▓рдХреНрд╖реНрдп SOAP рд╡реЗрдм рд╕реЗрд╡рд╛ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рдЧреНрд░рд╛рд╣рдХ рдмрдирд╛рдирд╛ рд╣реИ, рдЬреЛ рдХрд┐рд╕реА рднреА рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛ рднреЗрдЬрдиреЗ / рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдФрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдкрд░ рдХреЛрдИ рднреА рд╕рдВрдЪрд╛рд▓рди рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдЧрд╛ред рдЬреИрд╕рд╛ рдХрд┐ рдкрд╣рд▓реЗ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, CRUD рдЗрд╕рдХреЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рдЙрдкрдпреБрдХреНрдд рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЧреНрд░рд╛рд╣рдХ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦ рд╕рдХрддрд╛ рд╣реИ:

 var client = new SoapServiceClient("NeliburSoapService"); ClientResponse response = client.Post<ClientResponse>(createRequest); response = client.Put<ClientResponse>(updateRequest);
      
      





рдиреАрдЪреЗ SoapServiceClient



рд╡рд░реНрдЧ рдХреА Post



рд╡рд┐рдзрд┐ рдХреЗ рд▓рд┐рдП рд╕рднреА рдХреЛрдб рд╣реИред

 public TResponse Post<TResponse>(object request) { return Send<TResponse>(request, OperationTypeHeader.Post); } private TResponse Send<TResponse>(object request, MessageHeader operationType) { using (var factory = new ChannelFactory<ISoapService>(_endpointConfigurationName)) { MessageVersion messageVersion = factory.Endpoint.Binding.MessageVersion; Message message = CreateMessage(request, operationType, messageVersion); ISoapService channel = factory.CreateChannel(); Message result = channel.Process(message); return result.GetBody<TResponse>(); } } private static Message CreateMessage( object request, MessageHeader actionHeader, MessageVersion messageVersion) { Message message = Message.CreateMessage( messageVersion, ServiceMetadata.Operations.Process, request); var contentTypeHeader = new ContentTypeHeader(request.GetType()); message.Headers.Add(contentTypeHeader); message.Headers.Add(actionHeader); return message; }
      
      



рдХреГрдкрдпрд╛ CreateMessage



рдореЗрдердб рдкрд░ рдзреНрдпрд╛рди CreateMessage



рдФрд░ рдХреИрд╕реЗ DTO рдЯрд╛рдЗрдк рдФрд░ рдХреЙрд▓ рдХреА рдЧрдИ рд╕рд╛рдордЧреНрд░реА contentTypeHeader



рдФрд░ actionHeader



рдорд╛рдзреНрдпрдо рд╕реЗ contentTypeHeader



actionHeader



ред

SoapContentTypeHeader



рдФрд░ SoapOperationTypeHeader



рд▓рдЧрднрдЧ рд╕рдорд╛рди рд╣реИрдВред SoapContentTypeHeader



рдЙрдкрдпреЛрдЧ DTO рдкреНрд░рдХрд╛рд░ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ SoapOperationTypeHeader



рдХрд╛ рдЙрдкрдпреЛрдЧ рд▓рдХреНрд╖реНрдп рдСрдкрд░реЗрд╢рди рдХреЛ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдХрдо рд╢рдмреНрдж, рдЕрдзрд┐рдХ рдХреЛрдб:

 internal sealed class SoapContentTypeHeader : MessageHeader { private const string NameValue = "nelibur-content-type"; private const string NamespaceValue = "http://nelibur.org/" + NameValue; private readonly string _contentType; public SoapContentTypeHeader(Type contentType) { _contentType = contentType.Name; } public override string Name { get { return NameValue; } } public override string Namespace { get { return NamespaceValue; } } public static string ReadHeader(Message request) { int headerPosition = request.Headers.FindHeader(NameValue, NamespaceValue); if (headerPosition == -1) { return null; } var content = request.Headers.GetHeader<string>(headerPosition); return content; } protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) { writer.WriteString(_contentType); } }
      
      





рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд SoapServiceClient



рддрд░реАрдХреЗ рд╣реИрдВ:

 public static TResponse Get<TResponse>(object request) public static Task<TResponse> GetAsync<TResponse>(object request) public static void Post(object request) public static Task PostAsync(object request) public static TResponse Post<TResponse>(object request) public static Task<TResponse> PostAsync<TResponse>(object request) public static void Put(object request) public static Task PutAsync(object request) public static TResponse Put<TResponse>(object request) public static Task<TResponse> PutAsync<TResponse>(object request) public static void Delete(object request) public static Task DeleteAsync(object request)
      
      





рдЬреИрд╕рд╛ рдХрд┐ рдЖрдкрдиреЗ рдкрд╣рд▓реЗ рд╣реА рджреЗрдЦрд╛ рд╣реИ, рд╕рднреА CRUD рдСрдкрд░реЗрд╢рди рдореЗрдВ рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рд╕рдВрд╕реНрдХрд░рдг рд╣реИрдВред



SOAP рд╕реЗрд╡рд╛



SOAP рд╕реЗрд╡рд╛ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП:





рд╣рдорд╛рд░рд╛ рд▓рдХреНрд╖реНрдп рдХреБрдЫ рдРрд╕рд╛ рдмрдирд╛рдирд╛ рд╣реИ рдЬреЛ рдХрд┐рд╕реА рд╡рд┐рд╢реЗрд╖ Request



рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд CRUD рд╡рд┐рдзрд┐ рдХреЛ рдмреБрд▓рд╛рдПрдЧрд╛ред рдиреАрдЪреЗ рджрд┐рдП рдЧрдП рдЙрджрд╛рд╣рд░рдг рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдЖрдк Client



рдСрдмреНрдЬреЗрдХреНрдЯ рдХреИрд╕реЗ рдЬреЛрдбрд╝ рдФрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред



 public sealed class ClientProcessor : IPut<CreateClientRequest>, IGet<GetClientRequest> { private readonly List<Client> _clients = new List<Client>(); public object Get(GetClientRequest request) { Client client = _clients.Single(x => x.Id == request.Id); return new ClientResponse {Id = client.Id, Name = client.Name}; } public object Put(CreateClientRequest request) { var client = new Client { Id = Guid.NewGuid(), Name = request.Name }; _clients.Add(client); return new ClientResponse {Id = client.Id}; } }
      
      







рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рд░реБрдЪрд┐ IGet



рдФрд░ IGet



IPost



ред рд╡реЗ CRUD рдСрдкрд░реЗрд╢рди рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддреЗ рд╣реИрдВред рд╡рд░реНрдЧ рдЖрд░реЗрдЦ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВ:

рд╡рд░реНрдЧ рдЖрд░реЗрдЦ

рдЕрдм рдЖрдкрдХреЛ Request



рдХреЛ рд╕рдВрдмрдВрдзрд┐рдд CRUD рдСрдкрд░реЗрд╢рди рдХреЗ рд╕рд╛рде рдЬреЛрдбрд╝рдирд╛ рд╣реЛрдЧрд╛ред рдЕрдиреБрд░реЛрдз рдкреНрд░реЛрд╕реЗрд╕рд░ рдХреЗ рд╕рд╛рде Request



рдХреЛ рд╕рдВрдмрджреНрдз рдХрд░рдиреЗ рдХрд╛ рд╕рдмрд╕реЗ рдЖрд╕рд╛рди рддрд░реАрдХрд╛ рд╣реИред NeliburService



рдЗрд╕ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рд╖реНрдард┐рдд рд╣реИред рдЖрдЗрдП рдЗрд╕реЗ рджреЗрдЦреЗрдВред

 public abstract class NeliburService { internal static readonly RequestMetadataMap _requests = new RequestMetadataMap(); protected static readonly Configuration _configuration = new Configuration(); private static readonly RequestProcessorMap _requestProcessors = new RequestProcessorMap(); protected static void ProcessOneWay(RequestMetadata requestMetaData) { IRequestProcessor processor = _requestProcessors.Get(requestMetaData.Type); processor.ProcessOneWay(requestMetaData); } protected static Message Process(RequestMetadata requestMetaData) { IRequestProcessor processor = _requestProcessors.Get(requestMetaData.Type); return processor.Process(requestMetaData); } protected sealed class Configuration : IConfiguration { public void Bind<TRequest, TProcessor>(Func<TProcessor> creator) where TRequest : class where TProcessor : IRequestOperation { if (creator == null) { throw Error.ArgumentNull("creator"); } _requestProcessors.Add<TRequest, TProcessor>(creator); _requests.Add<TRequest>(); } public void Bind<TRequest, TProcessor>() where TRequest : class where TProcessor : IRequestOperation, new() { Bind<TRequest, TProcessor>(() => new TProcessor()); } } }
      
      





RequestMetadataMap



рдЙрдкрдпреЛрдЧ Request



рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬреЛ Message



рд╕реЗ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ Request



рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИред

 internal sealed class RequestMetadataMap { private readonly Dictionary<string, Type> _requestTypes = new Dictionary<string, Type>(); internal void Add<TRequest>() where TRequest : class { Type requestType = typeof(TRequest); _requestTypes[requestType.Name] = requestType; } internal RequestMetadata FromRestMessage(Message message) { UriTemplateMatch templateMatch = WebOperationContext.Current.IncomingRequest.UriTemplateMatch; NameValueCollection queryParams = templateMatch.QueryParameters; string typeName = UrlSerializer.FromQueryParams(queryParams).GetTypeValue(); Type targetType = GetRequestType(typeName); return RequestMetadata.FromRestMessage(message, targetType); } internal RequestMetadata FromSoapMessage(Message message) { string typeName = SoapContentTypeHeader.ReadHeader(message); Type targetType = GetRequestType(typeName); return RequestMetadata.FromSoapMessage(message, targetType); } private Type GetRequestType(string typeName) { Type result; if (_requestTypes.TryGetValue(typeName, out result)) { return result; } string errorMessage = string.Format( "Binding on {0} is absent. Use the Bind method on an appropriate NeliburService", typeName); throw Error.InvalidOperation(errorMessage); } }
      
      





RequestProcessorMap



рд╣реИрдВрдбрд▓рд░ рдХреЗ рд▓рд┐рдП Request



рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреЛ рдмрд╛рдВрдзрддрд╛ рд╣реИред

 internal sealed class RequestProcessorMap { private readonly Dictionary<Type, IRequestProcessor> _repository = new Dictionary<Type, IRequestProcessor>(); public void Add<TRequest, TProcessor>(Func<TProcessor> creator) where TRequest : class where TProcessor : IRequestOperation { Type requestType = typeof(TRequest); IRequestProcessor context = new RequestProcessor<TRequest, TProcessor>(creator); _repository[requestType] = context; } public IRequestProcessor Get(Type requestType) { return _repository[requestType]; } }
      
      





рдЕрдм рд╣рдо рдЕрдВрддрд┐рдо рдЪрд░рдг рдХреЗ рд▓рд┐рдП рддреИрдпрд╛рд░ рд╣реИрдВ: рд▓рдХреНрд╖реНрдп рдкрджреНрдзрддрд┐ рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ред рдпрд╣рд╛рдБ рд╣рдорд╛рд░реА SOAP рд╕реЗрд╡рд╛ рд╣реИ:

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public sealed class SoapService : ISoapService { public Message Process(Message message) { return NeliburSoapService.Process(message); } public void ProcessOneWay(Message message) { NeliburSoapService.ProcessOneWay(message); } }
      
      





рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЖрдЗрдП рд╕реЗрд╡рд╛ рдХреА рдУрд░ рд╕реЗ рдирд┐рд╖реНрдкрд╛рджрди рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдЕрдиреБрдХреНрд░рдо рдЖрд░реЗрдЦ рдХреЛ рджреЗрдЦреЗрдВред

рдЕрдиреБрдХреНрд░рдо рдЪрд╛рд░реНрдЯ

рдХреЛрдб рдЪрд░рдг рдореЗрдВ рдЧреЛрддрд╛ рд▓рдЧрд╛рддреЗ рд╣реИрдВред NeliburSoapService



рд╕рд┐рд░реНрдл рдПрдХ рдФрд░ рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЗрд╕ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВред

 public sealed class NeliburSoapService : NeliburService { private NeliburSoapService() { } public static IConfiguration Configure(Action<IConfiguration> action) { action(_configuration); return _configuration; } public static Message Process(Message message) { RequestMetadata metadata = _requests.FromSoapMessage(message); return Process(metadata); } public static void ProcessOneWay(Message message) { RequestMetadata metadata = _requests.FromSoapMessage(message); ProcessOneWay(metadata); } }
      
      





NeliburSoapService



рдХреЗрд╡рд▓ NeliburSoapService



рдХреЛ рд╕рдЬрд╛рддрд╛ рд╣реИ, рдЕрд░реНрдерд╛рдд, рдпрд╣ SOAP Message



рд▓рд┐рдП RequestMetadata



рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рд╡рд┐рдзрд┐ рдХрд╣рддрд╛ рд╣реИред

рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рдмрд╛рдд рдпрд╣рд╛рдБ рд╣реЛ рд░рд╣реА рд╣реИ:



SoapRequestMetadata рдореБрдЦреНрдп рд╡рд╕реНрддреБ рд╣реИ рдЬреЛ CRUD рдСрдкрд░реЗрд╢рди рдХреЗ рдкреНрд░рдХрд╛рд░, рдбреЗрдЯрд╛ рдХрд╛ рдЕрдиреБрд░реЛрдз, рдЗрд╕рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреЛ рдЬреЛрдбрд╝рддреА рд╣реИ, рдФрд░ рдПрдХ рдЕрдиреБрд░реЛрдз рдХрд╛ рдЬрд╡рд╛рдм рднреА рджреЗ рд╕рдХрддреА рд╣реИред

 internal sealed class SoapRequestMetadata : RequestMetadata { private readonly MessageVersion _messageVersion; private readonly object _request; internal SoapRequestMetadata(Message message, Type targetType) : base(targetType) { _messageVersion = message.Version; _request = CreateRequest(message, targetType); OperationType = SoapOperationTypeHeader.ReadHeader(message); } public override string OperationType { get; protected set; } public override Message CreateResponse(object response) { return Message.CreateMessage(_messageVersion, SoapServiceMetadata.Action.ProcessResponse, response); } public override TRequest GetRequest<TRequest>() { return (TRequest)_request; } private static object CreateRequest(Message message, Type targetType) { using (XmlDictionaryReader reader = message.GetReaderAtBodyContents()) { var serializer = new DataContractSerializer(targetType); return serializer.ReadObject(reader); } } }
      
      





рдФрд░ рдЕрдВрдд рдореЗрдВ, рд╣рдо рд╕рд┐рд░реНрдл RequestProcessor



рдорд╛рдзреНрдпрдо рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд CRUD рдСрдкрд░реЗрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВред RequestProcessor



рдСрдкрд░реЗрд╢рди рдХреЛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП RequestProcessor



рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЬрдм рдпрд╣ SoapServiceClient



рд╡рд░реНрдЧ рдХреЗ рд▓рд┐рдП рдкрд░рд┐рдгрд╛рдо рджреЗрддрд╛ рд╣реИ рддреЛ рдЗрд╕реЗ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИред

 internal sealed class RequestProcessor<TRequest, TProcessor> : IRequestProcessor where TRequest : class where TProcessor : IRequestOperation { private readonly Func<TProcessor> _creator; public RequestProcessor(Func<TProcessor> creator) { _creator = creator; } public Message Process(RequestMetadata metadata) { switch (metadata.OperationType) { case OperationType.Get: return Get(metadata); case OperationType.Post: return Post(metadata); case OperationType.Put: return Put(metadata); case OperationType.Delete: return Delete(metadata); default: string message = string.Format("Invalid operation type: {0}", metadata.OperationType); throw Error.InvalidOperation(message); } } public void ProcessOneWay(RequestMetadata metadata) { switch (metadata.OperationType) { case OperationType.Get: GetOneWay(metadata); break; case OperationType.Post: PostOneWay(metadata); break; case OperationType.Put: PutOneWay(metadata); break; case OperationType.Delete: DeleteOneWay(metadata); break; default: string message = string.Format("Invalid operation type: {0}", metadata.OperationType); throw Error.InvalidOperation(message); } } private Message Delete(RequestMetadata metadata) { var service = (IDelete<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.Delete(request); return metadata.CreateResponse(result); } private void DeleteOneWay(RequestMetadata metadata) { var service = (IDeleteOneWay<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.DeleteOneWay(request); } private Message Get(RequestMetadata metadata) { var service = (IGet<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.Get(request); return metadata.CreateResponse(result); } private void GetOneWay(RequestMetadata metadata) { var service = (IGetOneWay<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.GetOneWay(request); } private Message Post(RequestMetadata metadata) { var service = (IPost<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.Post(request); return metadata.CreateResponse(result); } private void PostOneWay(RequestMetadata metadata) { var service = (IPostOneWay<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.PostOneWay(request); } private Message Put(RequestMetadata metadata) { var service = (IPut<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.Put(request); return metadata.CreateResponse(result); } private void PutOneWay(RequestMetadata metadata) { var service = (IPutOneWay<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.PutOneWay(request); } }
      
      





рдбреЗрдореЛ



рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдбреЗрдЯрд╛ рдЕрдиреБрдмрдВрдзреЛрдВ рдХреА рдШреЛрд╖рдгрд╛ рдХрд░реЗрдВ:





рд╕рд░реНрд╡рд░ рдХрд╛ рдкрдХреНрд╖



рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓ рд╕рдмрд╕реЗ рдЖрдо рд╣реИ:

 <configuration> <!--WCF--> <system.serviceModel> <services> <service name="Nelibur.ServiceModel.Services.Default.SoapServicePerCall"> <endpoint address="http://localhost:5060/service" binding="basicHttpBinding" bindingConfiguration="ServiceBinding" contract="Nelibur.ServiceModel.Contracts.ISoapService" /> </service> </services> <bindings> <basicHttpBinding> <binding name="ServiceBinding"> <security mode="None"> <transport clientCredentialType="None" /> </security> </binding> </basicHttpBinding> </bindings> </system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
      
      







WCF рд╕реЗрд╡рд╛ рдЕрддреНрдпрдВрдд рд╕рд░рд▓ рд╣реИ:

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public sealed class SoapServicePerCall : ISoapService { /// <summary> /// Process message with response. /// </summary> /// <param name="message">Request message.</param> /// <returns>Response message.</returns> public Message Process(Message message) { return NeliburSoapService.Process(message); } /// <summary> /// Process message without response. /// </summary> /// <param name="message">Request message.</param> public void ProcessOneWay(Message message) { NeliburSoapService.ProcessOneWay(message); } }
      
      





рд╣реИрдВрдбрд▓рд░ рд╕реЗ рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рдмрд╛рдВрдзрдирд╛ред рд╕рд╛рджрдЧреА рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рдХреЗрд╡рд▓ рдПрдХ рдЕрдиреБрд░реЛрдз рд╣реИрдВрдбрд▓рд░ рдмрдирд╛рдпрд╛ред рдЖрдк рдЬрд┐рддрдиреЗ рдЪрд╛рд╣реЗрдВ рдЙрддрдиреЗ рдЕрдиреБрд░реЛрдз рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдореИрдВ рдЖрдкрдХреЛ CQRS рдкрд░ рдорд╛рд░реНрдЯрд┐рди рдлрд╛рдЙрд▓рд░ рдХреЗ рд▓реЗрдЦ рдХреЛ рдкрдврд╝рдиреЗ рдХреА рд╕рд▓рд╛рд╣ рджреЗрддрд╛ рд╣реВрдВред рдЗрд╕рд╕реЗ рдЖрдкрдХреЛ рд╕рд╣реА рдЪреБрдирд╛рд╡ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдорд┐рд▓реЗрдЧреАред рдЕрдиреБрд░реЛрдзреЛрдВ рдФрд░ рд╕рдВрдЪрд╛рд▓рдХреЛрдВ рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдб:

 private static void BindRequestToProcessors() { NeliburSoapService.Configure(x => { x.Bind<CreateClientRequest, ClientProcessor>(); x.Bind<UpdateClientRequest, ClientProcessor>(); x.Bind<DeleteClientRequest, ClientProcessor>(); x.Bind<GetClientRequest, ClientProcessor>(); }); }
      
      





рдФрд░ рдЕрдВрдд рдореЗрдВ, ClientProcessor



:

 public sealed class ClientProcessor : IPost<CreateClientRequest>, IGet<GetClientRequest>, IDeleteOneWay<DeleteClientRequest>, IPut<UpdateClientRequest> { private static List<Client> _clients = new List<Client>(); public void DeleteOneWay(DeleteClientRequest request) { Console.WriteLine("Delete Request: {0}\n", request); _clients = _clients.Where(x => x.Id != request.Id).ToList(); } public object Get(GetClientRequest request) { Console.WriteLine("Get Request: {0}", request); Client client = _clients.Single(x => x.Id == request.Id); return new ClientResponse { Id = client.Id, Email = client.Email }; } public object Post(CreateClientRequest request) { Console.WriteLine("Post Request: {0}", request); var client = new Client { Id = Guid.NewGuid(), Email = request.Email }; _clients.Add(client); return new ClientResponse { Id = client.Id, Email = client.Email }; } public object Put(UpdateClientRequest request) { Console.WriteLine("Put Request: {0}", request); Client client = _clients.Single(x => x.Id == request.Id); client.Email = request.Email; return new ClientResponse { Id = client.Id, Email = client.Email }; } }
      
      







рдЧреНрд░рд╛рд╣рдХ рдХрд╛ рдкрдХреНрд╖



рдЧреНрд░рд╛рд╣рдХ рдХреЛрдб рд╕рд░рд▓ рд╣реИ:

 private static void Main() { var client = new SoapServiceClient("NeliburSoapService"); var createRequest = new CreateClientRequest { Email = "email@email.com" }; Console.WriteLine("POST Request: {0}", createRequest); ClientResponse response = client.Post<ClientResponse>(createRequest); Console.WriteLine("POST Response: {0}\n", response); var updateRequest = new UpdateClientRequest { Email = "new@email.com", Id = response.Id }; Console.WriteLine("PUT Request: {0}", updateRequest); response = client.Put<ClientResponse>(updateRequest); Console.WriteLine("PUT Response: {0}\n", response); var getClientRequest = new GetClientRequest { Id = response.Id }; Console.WriteLine("GET Request: {0}", getClientRequest); response = client.Get<ClientResponse>(getClientRequest); Console.WriteLine("GET Response: {0}\n", response); var deleteRequest = new DeleteClientRequest { Id = response.Id }; Console.WriteLine("DELETE Request: {0}", deleteRequest); client.Delete(deleteRequest); Console.ReadKey(); }
      
      







рдирд┐рд╖реНрдкрд╛рджрди рдкрд░рд┐рдгрд╛рдо:

рдЧреНрд░рд╛рд╣рдХ:

рдЧреНрд░рд╛рд╣рдХ



рд╕реЗрд╡рд╛:

рд╕реЗрд╡рд╛



рд╡рд╣ рд╕рдм



рдореБрдЭреЗ рдЖрд╢рд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдЕрдЪреНрдЫрд╛ рд▓рдЧрд╛ рд╣реЛрдЧрд╛ред рдпрд╣рд╛рдВ рдЖрдк рд╕реАрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ WCF рдФрд░ Nelibur рдкрд░ Restful рд╡реЗрдм рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХреИрд╕реЗ рдХрд░реЗрдВ ред рд▓реЗрдЦ (рдЕрдиреБрд╡рд╛рдж) рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред рд╕реНрд░реЛрддреЛрдВ рдХреЛ рдореВрд▓ рдкреГрд╖реНрда рдпрд╛ GitHub рд╕реЗ рдбрд╛рдЙрдирд▓реЛрдб рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред



All Articles