Dynamically update WCF Routing Service configuration at runtime - c#

I have been following the WCF Routing Service tutorials on MSDN:
Dynamic Configuration
Source (See code below)
After much pain trying to convert the console examples to IIS hosted prototypes, I now have a WCF Routing Service which updates its configuration once every 5 seconds as per the tutorial.
I now need to trigger this update from a Web page instead of the timer auto updating every 5 seconds but cannot find any examples of how to do this. Something like an admin screen which handles CRUD ops for endpoints stored in a DB. If a user makes a change to the config, the Routing Service will need to update its configuration dynamically.
Apparently you can do something like this with UDP Announcements and Discovery Services, but I don't want that A simple endpoint which triggers the update called from another app will suffice.
How do I get a reference to the routing service UpdateBehavior in order to manually call the UpdateRules method?
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Routing;
using System.Threading;
namespace ErpRoutingService
{
public class UpdateBehavior : BehaviorExtensionElement, IServiceBehavior
{
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
RulesUpdateExtension rulesUpdateExtension = new RulesUpdateExtension();
serviceHostBase.Extensions.Add(rulesUpdateExtension);
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
class RulesUpdateExtension : IExtension<ServiceHostBase>, IDisposable
{
bool primary = false;
ServiceHostBase owner;
Timer timer;
void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner)
{
this.owner = owner;
//Call immediately, then every 5 seconds after that.
this.timer = new Timer(this.UpdateRules, this, TimeSpan.Zero, TimeSpan.FromSeconds(5));
}
void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner)
{
this.Dispose();
}
public void Dispose()
{
if (this.timer != null)
{
this.timer.Dispose();
this.timer = null;
}
}
void UpdateRules(object state)
{
//Updating Routing Configuration
RoutingConfiguration rc = new RoutingConfiguration();
var inspector = new ErpMessageInspectorBehavior();
if (this.primary)
{
ServiceEndpoint endPoint101 = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IRequestReplyRouter)),
new BasicHttpBinding(),
new EndpointAddress("http://meldev:62395/ErpIntegrationService.svc"));
endPoint101.EndpointBehaviors.Add(inspector);
rc.FilterTable.Add(new MatchAllMessageFilter(), new List<ServiceEndpoint> { endPoint101 });
}
else
{
ServiceEndpoint endPoint102 = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IRequestReplyRouter)),
new BasicHttpBinding(),
new EndpointAddress("http://meldev:62396/ErpIntegrationService.svc"));
endPoint102.EndpointBehaviors.Add(inspector);
rc.FilterTable.Add(new MatchAllMessageFilter(), new List<ServiceEndpoint> { endPoint102 });
}
this.owner.Extensions.Find<RoutingExtension>().ApplyConfiguration(rc);
this.primary = !this.primary;
}
}
public override Type BehaviorType
{
get { return typeof(UpdateBehavior); }
}
protected override object CreateBehavior()
{
return new UpdateBehavior();
}
}
}

Starting from your ServiceHost instance it's pretty easy:
var updateBahvaior = serviceHost.Description.Behaviors.Find<UpdateBehavior>();
Then, if you expose a method that calls UpdateRules method from the inner private class, you can call it.

You might have to use public static variables in your custom class...

Found this document online.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/routing-introduction#dynamic-configuration
RoutingExtension ApplyConfiguration would help here.
Attaching snippet.

Related

How to pass winform custom user credentials to WCF services in every requests?

What I'm trying to achieve is passing credentials/token to WCF services in every requests. BTW, this credential IS NOT windows credentials, they are fetched from custom db, and the authentication logic is quite simple, tenantId+username+password.
I'm currently using message inspector to insert these kind of information in the headers and fetch them from server-side inspector(using OperationContext).
But in order to stay thread-safe,I have to wrap the requests in every winform request like this:
using (new OperationContextScope((WcfService as ServiceClient).InnerChannel))
{
MessageHeader hdXXId = MessageHeader.CreateHeader("XXId", "CustomHeader", WinformSomeVariable.XXId);
OperationContext.Current.OutgoingMessageHeaders.Add(hdXXId);
_objXX = WcfService.GetXXById(id);
}
Like showed above, this is quite heavy and obviously not a smart way to handle this situation. So is there any way to hold these kind of information safely and can as well fetch them in the WCF Inspectors?
Many thanks!
PS. Thanks to #Abraham Qian, I was being silly the whole time. Just put the client inspector within the same winform project will solve this issue.
Just ignore the question of how to refactor your authentication for a moment.
As for how to use the IClientMessageInspector interface to create a persistent message header, the following code snippet might be useful (Assume that invocation by using Channel Factory)
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:1300");
IService service = ChannelFactory<IService>.CreateChannel(new BasicHttpBinding(), new EndpointAddress(uri));
try
{
Console.WriteLine(service.SayHello());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
[ServiceContract(Namespace = "mydomain")]
[CustomContractBehavior]
public interface IService
{
[OperationContract]
string SayHello();
}
public class ClientMessageLogger : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
string displayText = $"the client has received the reply:\n{reply}\n";
Console.Write(displayText);
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
//Add custom message header
request.Headers.Add(MessageHeader.CreateHeader("myheader","mynamespace",2000));
string displayText = $"the client send request message:\n{request}\n";
Console.WriteLine(displayText);
return null;
}
}
public class CustomContractBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
{
public Type TargetContract => typeof(IService);
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageLogger());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
return;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}

How can WCF service detect client disconnection

I have the following complete program (which can be copy-pasted-built-and-run. you might have to add a few references). The objective of the program is for the service to detect (e.g. receive a SocketException or IOException of some form or as attempted in the code via some event handling) that a connected client (tested/test from web-browser) has disconnected before the response has been fully delivered (see return statements in method Talk(string animal)). To reproduce the issue, there is a configurable parameter (see new AnimalTalkService(3)) which dictates how long the service will take to respond to a given request. And within this time frame, i can close the browser in order to raise the client-disconnection event (see method ClientDisconnected() in class ClientConnectionTracker). I am unable to get any exceptions thrown into the implementation of the service or get the Closed and Faulted events triggered. Would someone have an idea on how to go about to (implement) get the desired effect?
// Code:
using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Threading;
namespace TestClientDisconnect
{
class ClientConnectionTracker : IChannelInitializer
{
public void Initialize(IClientChannel channel)
{
channel.Closed += ClientDisconnected;
channel.Faulted += ClientDisconnected;
}
private void ClientDisconnected(object sender, EventArgs e)
{
Console.WriteLine("Client Disconnected");
throw new NotImplementedException();
}
}
class ClientConnectionTrackerEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ChannelDispatcher.ChannelInitializers.Add(new ClientConnectionTracker());
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
[ServiceContract]
interface IAnimalTalkService
{
[OperationContract]
[WebInvoke(UriTemplate = "/{animal}", Method = "GET")]
string Talk(string animal);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class AnimalTalkService : IAnimalTalkService
{
private int delayInSeconds;
public AnimalTalkService(int delayInSeconds = 0)
{
this.delayInSeconds = delayInSeconds;
}
public string Talk(string animal)
{
Console.WriteLine("Creating sentence for animal {0} ...", animal);
if (delayInSeconds > 0)
{
// Simulate heavy duty work:
Thread.Sleep(1000 * delayInSeconds);
}
switch(animal.ToLower())
{
case "sheep":
return "baa";
case "dog":
return "woof";
case "cat":
return "miao";
default:
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
return null;
}
}
}
class Program
{
static void Main(string[] args)
{
AnimalTalkService serviceInstance = new AnimalTalkService(3);
Uri address = new Uri("http://127.0.0.1:1234/");
WebServiceHost host = new WebServiceHost(serviceInstance, address);
WebHttpBinding binding = new WebHttpBinding();
ServiceEndpoint endPoint = host.AddServiceEndpoint(typeof(IAnimalTalkService), binding, "");
endPoint.EndpointBehaviors.Add(new WebHttpBehavior() { DefaultOutgoingResponseFormat = WebMessageFormat.Json });
endPoint.EndpointBehaviors.Add(new ClientConnectionTrackerEndpointBehavior());
host.Open();
Console.WriteLine("Service is running at {0}. Press Enter key to exit", host.BaseAddresses[0]);
Console.ReadLine();
}
}
}
Thanks in advance.
The word "disconnection" implies a session, isn't it?
The best way, in my opinion, to have methods, that explicitly creates and terminates the session using an explicit session id (here I used an arbitrary type):
[ServiceContract]
public interface IWebService
{
[OperationContract]
SessionId BeginNewSession();
[OperationContract]
void DoSomething(SessionId id, ...);
[OperationContract]
void EndSession(SessionId id);
}
This is certainly recommended for HTTP protocol, which doesn't support transport-level sessions.
In this case you can write another class, that will housekeep outdated sessions which haven't been closed.
In case you use a binding that supports transport-level sessions, there is another option - to set up session-bound service instance management (and use corresponding binding), implement IDisposable interface in the service class and place the related code inside Dispose() method:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TheService : IService, IDisposable
{
...
public void Dispose()
{
// code of session termination
...
}
}
Finally you can combine both options by marking the explicit session-terminating method with [OperationContract(IsTerminating = true)] attribute:
[ServiceContract(..., SessionMode=SessionMode.Required)]
public interface IService
{
[OperationContract(IsTerminating = true)]
void Close();
...
}

Type casting issue in C# and wcf header in ASP.NET

I am now developing a WCF service using Visual Studio 2013. My service contains a custom header field must be passed. Field name is "lisenseKey" I developed it. WCF service is working fine. Now I am about to call my service to test. I added a service reference to client project. Now I am calling like this.
MyService.Myservice proxy = new MyService.Myservice();
proxy.LisenseKey = "xxxxxx";
Label1.Text = proxy.GetMessage(TextBox1.Text);
When I assign lisense key value like this, it is showing error with red underline that "Cannot implicitly convert type string to MyService.MyService.string". How can I type cast in this condition? Or is there any way to pass header ? And I do not know how to use "MessageHeader".
Here is my server side code. I am trying to consume "GetMessage" method.
//using System.ServiceModel;
//using System.ServiceModel.Dispatcher;
//using System.ServiceModel.Channels;
//using System.ServiceModel.Description;
//using System.Collections.ObjectModel;
[ServiceContract]
public interface IMyservice
{
[OperationContract]
void DoWork();
[OperationContract]
string GetMessage(string name);
}
public class MyServiceMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if(request.Headers.FindHeader("LisenseKey","")==-1)
{
throw new FaultException("Lisense Key Was Not Provided");
}
var lisenseKey = request.Headers.GetHeader<string>("LisenseKey", "");
if (string.IsNullOrEmpty(lisenseKey))
{
throw new FaultException("Lisnse key is not valid");
}
if(lisenseKey!="12345x")
{
throw new FaultException("Lisense key is not valid");
}
return instanceContext;
}
public void BeforeSendReply(ref Message reply,object correlationState)
{
}
}
public class MyServiceMessageInspectorBehaviour:Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach(ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach(var endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyServiceMessageInspector());
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}

WCF service - initialization issue

I am currently trying to create a WCF service and then host it from a commandLine application. During hosting of the service am trying to initialize it with an instance of an object called MyProxy. All calls to the service should delegate to MyProxy.
I have created a service and made calls. But I am not able to initialize the MyProxy instance in the service. It is always null. So when any calls to the service is made, I am not able to delegate it to the proxy.
I have been trying to get this work since last two days. Am lost now, not sure what is happening. Kindly help.
public class MasOperationsService : IMasOperations
{
//This MyProxy instance should be used to delegate all calls to service.
public MyProxy myProxyInstance;
public MasOperationsService()
{
myProxyInstance = null;
}
public MasOperationsService(MyProxy proxy)
{
myProxyInstance = proxy;
}
public CoAuthorSearchResult ExtractCoAuthorsFromAuthor(long authorCellId, uint levelsToExtract)
{
//The service will delegate the call to MyProxy.
//myProxyInstance is always null
return myProxyInstance.GetProxyData(...);
}
}
public class MyInstanceProvider : IInstanceProvider
{
public object GetInstance(InstanceContext instanceContext, Message message)
{
MyProxy name = message.Headers.GetHeader<MyProxy>("Name", "http://my.namespace");
if (name != null)
{
return new MasOperationsService(name);
}
return null;
}
public object GetInstance(InstanceContext instanceContext)
{
return new MasOperationsService(null);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}
public class MyServiceBehavior : IServiceBehavior
{
MyInstanceProvider myProvider = new MyInstanceProvider();
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider = this.myProvider;
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}
The service is launched using,
class MyServiceLauncher
{
ServiceHost host;
IMasOperations proxy;
ChannelFactory<IMasOperations> factory;
public void StartService(MyProxy proxyInstance)
{
string baseAddress = "http://localhost:8730/Design_Time_Addresses/MASService/Service1";
host = new ServiceHost(typeof(MasOperationsService), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(IMasOperations), GetBinding(), "");
host.Description.Behaviors.Add(new MyServiceBehavior());
host.Open();
Console.WriteLine("Host opened");
factory = new ChannelFactory<IMasOperations>(GetBinding(), new EndpointAddress(baseAddress));
proxy = factory.CreateChannel();
using (OperationContextScope scope = new OperationContextScope((IContextChannel)proxy))
{
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Name", "http://my.namespace", proxyInstance));
}
}
public void ShutDownService()
{
((IClientChannel)proxy).Close();
factory.Close();
host.Close();
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
return result;
}
}
I also put a Debugger.Launch() almost everywhere, just to see it being initialized (Both constructor of Service and in IInstanceProvider). It dosent get fired.
Here are the missing types. Also in the service, there is a call
return myProxyInstance.GetProxyData(...);
removing the dots compiles the application.
[ServiceContract]
public interface IMasOperations
{
[OperationContract]
CoAuthorSearchResult ExtractCoAuthorsFromAuthor(long AuthorCellId, uint LevelsToExtract);
}
public class CoAuthorSearchResult
{ }
public class MyProxy
{
public CoAuthorSearchResult GetProxyData()
{
return new CoAuthorSearchResult();
}
}
#kobac
asked for a piece of code showing where GetInstance is being called. I am not sure how and why I need to do this.
Currently I just create an object of the service class - MasOperationsClient at the client and call the method ExtractCoAuthorsFromAuthor().

Ninject, WCF & Auto Discovery

I can't seem to get these 3 working together.
I narrowed it down to a very simple service with 1 method:
[System.ServiceModel.ServiceContract]
public interface Icontract
{
[System.ServiceModel.OperationContract]
void Ping();
}
public class contract : Icontract
{
public void Ping()
{ }
}
I have a factory that looks like this:
public class ServiceFactory
{
private readonly IKernel _kernel;
public ServiceFactory(IKernel kernel)
{
_kernel = kernel;
}
public NinjectServiceHost<T> GetService<T>()
{
return _kernel.Get<NinjectServiceHost<T>>();
}
}
If I create the service like so...
_tmp = new ServiceHost(typeof(ConsoleApplication1.contract));
_tmp.Open();
...Discovery works just fine. However if I use the factory like so...
_tmp = _factory.GetService<ConsoleApplication1.contract>();
_tmp.Open();
...the service isn't discoverable anymore. Everything else about the service works as expected.
Anyone had any joy getting Discovery working this way, or is there something I'm doing wrong?
It is a bug in Ninject.Extensions.Wcf.NinjectServiceBehavior class.
Just create your own NinjectServiceBehaviorFixed class derived from IServiceBehavior interface with following method:
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (EndpointDispatcher endpointDispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>().SelectMany(channelDispatcher => (IEnumerable<EndpointDispatcher>) channelDispatcher.Endpoints))
{
if (endpointDispatcher.DispatchRuntime.InstanceProvider == null)
{
endpointDispatcher.DispatchRuntime.InstanceProvider = _instanceProviderFactory(serviceDescription.ServiceType);
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(_requestScopeCleanUp);
}
}
}
Add to your NinjectModule code:
Unbind<IServiceBehavior>();
Bind<IServiceBehavior>().To<NinjectServiceBehaviorFixed>();
In ApplyDispatchBehavior method we just added check if endpointDispatcher.DispatchRuntime.InstanceProvider is null because WS-Discovery creates new endpoints with defined InstanceProvider which should not be overwritten
Where are you setting up your bindings? Somewhere in your code you'll need to initialize the kernel with a ServiceModule like this:
_kernel = new StandardKernel(new YourNinjectModule());
And then in the module code:
public class YourNinjectModule: NinjectModule
{
/// <summary>
/// Loads the module into the kernel.
/// </summary>
public override void Load()
{
// ===========================================================
//
// Bind dependency injection.
// Add entries in here for any new services or repositories.
//
// ===========================================================
this.Bind<Icontract>().To<contract>();
}
}

Categories

Resources