[WCF] opakowanie channel'a komunikacyjnego

0

uzywam ChannelFactory<tchannel> a nastepnie CreateChannel() do stworzenia sobie kanalu komunikacyjnego

chcialbym tak opakowac zwracany channel, aby moj obiekt wygladal jak implementacja TChannel, czyli udostepnial wszystkie jego metody, ale zebym dostal info o wywolaniu jakiejs metody, zebym mogl sie wpiac pomiedzy wywolanie metody, a stukniecie do serwera

generalnie musze dodawac cos do headera, ale nie chce modyfikowac kazdego wywolania (w zaden sposob)

public static class SrvCaller
    {
        public static string H1Value { get; set; }

        public static TReturn Call<TChannel, TReturn>(this TChannel srv, Func<TChannel, TReturn> code)
        {
            return srv.Call(H1Value, code);
        }
        public static TReturn Call<TChannel, TReturn>(this TChannel srv, string h1Value, Func<TChannel, TReturn> code)
        {
            using (OperationContextScope sc2 = new OperationContextScope((IClientChannel)srv))
            {
                var h = new MessageHeader<string>(h1Value);
                OperationContext.Current.OutgoingMessageHeaders.Add(h.GetUntypedHeader("H1", "http://foo.org"));
                return code(srv);
            }
        }
    }

ChannelFactory<ISrv2> factory2 = new ChannelFactory<ISrv2>("CliEndpoint");
ISrv2 srv2 = factory2.CreateChannel();
var res = srv2.Call(x => x.Test1("bla"));

chcialbym aby wywolanie pozostalo srv2.Test1("bla") ale zeby przed wywolaniem kodu na serwerze mogl dodav moj header
rozwazam dodanie IClientMessageInspector, ale pozostaje problem wziecia wartosci naglowka H1

jakies pomysly

0

Chyba dodanie messageInspectora bedzie najelegantsze
CHyba mi się nudziło że to zrobiłem, nawet działa chyba.
A tak na marginesie po co jest ten content, w message headers na serwisie tego nie widzę.
?

 klasa implementująca messageInspectora
    class MessageHeaderAdd_Inspector : IClientMessageInspector
    {
        protected MessageHeader messaeg_header = null;

        public MessageHeaderAdd_Inspector(MessageHeader messaeg_header)
        {
            this.messaeg_header = messaeg_header;
        }

        #region IClientMessageInspector Members

        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
        }
        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            request.Headers.Add(messaeg_header);
            return null;        
        }

        #endregion
    }
klasa implementujaca endpoint behavior, przekazujemy jej message header ktory bedzie dodawany
class MessageHeaderAdd_Behavior : IEndpointBehavior
    {
        protected MessageHeader message_header = null;

        public MessageHeaderAdd_Behavior(MessageHeader message_header)
        {
            this.message_header = message_header;
        }

        #region IEndpointBehavior Members

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(
                new MessageHeaderAdd_Inspector(this.message_header)
                );
        }
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
        }
        public void Validate(ServiceEndpoint endpoint)
        {
        }

        #endregion
    }

dalej, klasa abstraykcyjna obudowujaca channel, tylko przykladowo, mozna by dziedziczyc po IChannel aby to było porządniej

    abstract class AClientSideChannel<T>
    {
        protected ChannelFactory<T> ChannelFactory = null;
        protected T ClientCallChannel;

        public abstract void SetFactoryInstance(ChannelFactory<T> channel_factory_instance);
        public abstract void CreateChannel();
        public abstract void AddBehavior(IEndpointBehavior behavior);
    }

i przykladowa klasa implementująca Aclientsidechannel z <itestservice>

    class TSClientSideChannel : AClientSideChannel<ITestService>,ITestService
    {
        public TSClientSideChannel()
        {
        }

        public override void SetFactoryInstance(ChannelFactory<ITestService> channel_factory_instance)
        {
            this.ChannelFactory = channel_factory_instance;
        }
        public override void CreateChannel()
        {
            this.ClientCallChannel = this.ChannelFactory.CreateChannel();
        }
        public override void AddBehavior(IEndpointBehavior behavior)
        {
            this.ChannelFactory.Endpoint.Behaviors.Add(behavior);
        }

        #region ITestService Members

        public string SendMessage(string message)
        {
            return this.ClientCallChannel.SendMessage(message);
        }
        public int GetMagicNumber()
        {
            return this.ClientCallChannel.GetMagicNumber();
        }

        #endregion
    }

Uruchomienie tego jest stosunkowo proste

 Tworzymy instancje channel factory dając typ ITestService
            ChannelFactory<ITestService> channel_factory = new ChannelFactory<ITestService>(new NetTcpBinding(),
                new EndpointAddress("net.tcp://79.185.71.117:6789/TestService")
                );
dodajemy behavior ktoremu przekazujemy messageheader jako parameter 
            channel_factory.Endpoint.Behaviors.Add(new MessageHeaderAdd_Behavior(
                    new MessageHeader<string>("CONT").GetUntypedHeader("name param", "ns param")
                    )
                );

           AClientSideChannel<ITestService> ClientSide = new TSClientSideChannel();
                ClientSide.SetFactoryInstance(channel_factory);
                ClientSide.CreateChannel();

            string return_value = ((TSClientSideChannel)ClientSide).SendMessage("Hello");

            Console.WriteLine(return_value);

Mam nadzieje że o to chodziło ;P

0

nie do konca
mam juz sporo kodu, ktory uzywa kilku serwisow WCF, czyli sa tworzone factory, channele, etc.
pojawilo sie wymaganie, aby w headerze dodawane byly dodatkowe informacje (chodzi o identyfikacje pewnych lancuchow wywolan etc.)
chcialbym stworzyc takie rozwiazanie, aby nie wymagalo to modyfikacji juz istniejacego kodu (o ile to mozliwe)
sam header moge dodawac w client message inspector, ale jeszcze pozostaje kwestia pozyskania tych danych do headera, to nie dane statyczne, powinny byc uzyskane z innego serwisu
problem jest tez taki, ze serwisy komunikuja sie miedzy soba i tam tez musi byc ten header przekazywany
czyli
przylkady wywolan
Klient (K) pozyskuje header -> SrvA (A) -> SrvB (B)
K1 -> B -> A -> C
K2 -> B -> A -> C
do tego serwer A ma jenen kanal komunikacyjny do serwera B, ale jesli klient 1 przekazal jakis header do A, to A wywolujac B, powinno do tego wywolania dodac tez ten header

0

chyba uda mi sie ubrac to sensownie w slowa :)

klient pobiera token i ten token ma byc w naglownku wiadomosci przesylany
zakladamy ze serwisy rowniez sie ze soba komunikuja (dowolnie dlugi lancuch wywolan, pomiedzy roznymi serwisami) i ten token nadal ma byc przekazwywany w tych wywolaniach

poszukuje rozwiazania, ktore w najmniejszy sposob wplynie na juz istniejacy kod (najlepiej w ogole), wiec mile widziane za pomoca jakis behavior extensions

zakladam takze ze serwisy do kumunikacji pomiedzy soba uzywaja jednego kanalu komunikacyjnego (oczywiscie jest on ponownie tworzony jesli przejdzie w stan faulted etc.)

0

jest to behavior extension dodawany to endpointow zarowno klienta jak i serwera
klient powienie ustawic ClientTokenHolder.Token na token ktorym chce sie poslugiwac

czekam na krytyke :)
zaraz dodam projekt testowy
http://www.filefactory.com/file/b235790/n/WcfToken.rar
glowny program odpala kilka procesow klienckich

    public class ClientTokenHolder
    {
        public static string Token { get; set; }
    }

    public class TokenEndpointBehaviorExt : BehaviorExtensionElement, IEndpointBehavior, IClientMessageInspector, IDispatchMessageInspector
    {
        internal enum Direction { In, Out }

        private const string HeaderTokenElementName = "Token";
        private const string HeaderTokenElementNamespace = "http://foo.org";

        private string Token
        {
            get { return OperationContext.Current != null ? OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(HeaderTokenElementName, HeaderTokenElementNamespace) : ClientTokenHolder.Token; }
        }

        private void AddTokenHeader(System.ServiceModel.Channels.Message msg, string token)
        {
            MessageHeader<string> hToken = new MessageHeader<string>(token);
            msg.Headers.Add(hToken.GetUntypedHeader(HeaderTokenElementName, HeaderTokenElementNamespace));
        }

        #region IEndpointBehavior Members

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        { }
        public void Validate(ServiceEndpoint endpoint)
        { }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(this);
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        #endregion

        #region BehaviorExtensionElement Members
        public override Type BehaviorType
        {
            get { return typeof(TokenEndpointBehaviorExt); }
        }

        protected override object CreateBehavior()
        {
            return this;
        }
        #endregion

        #region IClientMessageInspector Members

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
        {
            Log(Token, request.Headers.MessageId.ToString(), request.Headers.Action, Direction.In);
            AddTokenHeader(request, Token);
            return request.Headers.MessageId.ToString();
        }

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            Log(reply.Headers.GetHeader<string>(HeaderTokenElementName, HeaderTokenElementNamespace), correlationState.ToString(), reply.Headers.Action, Direction.Out);
        }

        #endregion

        #region IDispatchMessageInspector Members

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            Log(Token, request.Headers.MessageId.ToString(), request.Headers.Action, Direction.In);
            return request.Headers.MessageId.ToString();
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            Log(Token, correlationState.ToString(), reply.Headers.Action, Direction.Out);
            AddTokenHeader(reply, Token);
        }

        #endregion

        private void Log(string token, string msgId, string action, Direction direction)
        {
            Console.WriteLine("{3} {4} | {0} | {1} | {2}", 
                token, // 0
                msgId, // 1
                action, // 2
                direction == Direction.In ? ">>>" : "<<<", // 3
                OperationContext.Current != null ? "Srv" : "Cli" // 4
                );
        }
    }

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0