I need to create a class that enable to Intercept httpRequest within the application (so no need for proxy) and log request and response details.
Create HttpRequest can created using any way (HttpClient, HttpWebRequest, Helper library like RestSharp .. anything), that's way I hope to create dynamic class contains event that fire when HttpRequest initialized.
I already use Network Tracing but all I can log are strings, So Is there a safer way to intercept request and response objects instead of parsing strings
public class DebugTraceListener : System.Diagnostics.TextWriterTraceListener
{
public override void Write(string message)
{
using (StreamWriter w = File.AppendText("D:\\Log\\log2.txt"))
{
Log(message, w);
}
}
public override void WriteLine(string message)
{
using (StreamWriter w = File.AppendText("D:\\Log\\log2.txt"))
{
Log(message, w);
}
}
public static void Log(string logMessage, TextWriter w)
{
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
}
}
I am assuming you are using asp.net webforms or asp.net mvc. You need to create a class which extends the class HttpApplication and implements the interface IHttpModule
I have created the following module:
using System.Web;
namespace WebApplication1.App_Start
{
using System;
public class RequestHandler : HttpApplication,IHttpModule
{
/// <summary>
/// Initializes a module and prepares it to handle requests.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application </param>
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
context.EndRequest += context_RequestCompleted;
}
void context_RequestCompleted(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var context = application.Context;
var response = context.Response;
context.Response.Write(response.Charset);
}
void context_BeginRequest(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var context = application.Context;
var url = context.Request.Url;
context.Response.Write(url.AbsoluteUri);
}
/// <summary>
/// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
/// </summary>
public void Dispose()
{
throw new NotImplementedException();
}
}
}
Next, register the HttpModule in your web.config by adding the following line in under :
<system.webServer>
<modules>
<add name="Handler" type="WebApplication1.App_Start.RequestHandler"/>
<remove name="FormsAuthenticationModule" />
</modules>
</system.webServer>
Every request you make, it will write the url and the charset of the response received.
replace the following line by your logging instructions:
context.Response.Write(url.AbsoluteUri);
context.Response.Write(response.Charset);
for e.g.:
Logger.Log(url.AbsoluteUri) //in context_BeginRequest
Logger.Log(response.ContentType) //in context_RequestCompleted
You can log different attributes of the response and url variable which i have defined above.
Hope it helps
Related
I have an ASP.NET Web Application (.NET Framework 4.8) in which I've set up NInject but any services I set up with InRequestScope are coming through as if transient scope (i.e. new instances are created for every entity that depends a dependency on it - within the same web request).
The NuGet packages I am using are as follows (latest):
Ninject v3.3.4
Ninject.Web.Common v.3.32 ("Bootstrapper for web projects")
App_Start\Ninject.Web.Common.cs is present and correct and is as follows:
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(ScormWebService.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(ScormWebService.App_Start.NinjectWebCommon), "Stop")]
namespace ScormWebService.App_Start
{
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
using Ninject.Web.Common.WebHost;
using Services;
using System;
using System.Web;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
public static IKernel Kernel { get; private set; }
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Bind<IDumpConfigService>().To<DumpConfigService>().InSingletonScope();
// These objects are created fresh for each request
kernel.Bind<ILogService>().To<LogService>().InRequestScope();
kernel.Bind<IDumpService>().To<DumpService>().InRequestScope();
kernel.Bind<ISaveDataRequestReader>().To<SaveDataRequestReaderXml>().InRequestScope();
kernel.Bind<ISaveDataResponseWriter>().To<SaveDataResponseWriterXml>().InRequestScope();
kernel.Bind<IHttpContextAccessor>().To<HttpContextAccessor>().InRequestScope();
Kernel = kernel;
}
}
}
The incoming request is actually an implementation of IHttpHandler (i.e. an ashx rather than aspx file). However, it is still a page request with a current request and an HttpContext.Current.
Here is how I am setting up the entities for the page request
public class SaveDataHandler : IHttpHandler, IRequiresSessionState
{
/// <summary>
/// A new handler is required for each and every incoming request
/// </summary>
public bool IsReusable => false;
public SaveDataHandler()
{
var kernel = App_Start.NinjectWebCommon.Kernel;
LogService = (ILogService)kernel.GetService(typeof(ILogService));
Reader = (ISaveDataRequestReader)kernel.GetService(typeof(ISaveDataRequestReader));
Writer = (ISaveDataResponseWriter)kernel.GetService(typeof(ISaveDataResponseWriter));
DumpService = (IDumpService)kernel.GetService(typeof(IDumpService));
}
}
So for example, three instances of ILogService are created per request during the SaveDataHandler constructor instead of one: SaveDataHandler itself requests it (see above) as does class DumpService : IDumpService and class SaveDataRequestReaderXml : ISaveDataRequestReader.
Can anyone provide insight as to why InRequestScope is acting like a transient scope? I suspect the cause is using a IHttpHandler (ashx) instead of Web Form (aspx) page but I can't see why that wouldn't work as HttpContext.Current is the same across the request and that is what NInject.Web.Common uses as a request scope identifier. I've created a WebForm.aspx page but the same issue occurs for this too so it's not specific to ashx/IHttpHandler requests:
namespace ScormWebService
{
public partial class WebForm1 : Page
{
protected void Page_Init(object sender, EventArgs e)
{
var kernel = App_Start.NinjectWebCommon.Kernel;
var LogService = (ILogService)kernel.GetService(typeof(ILogService));
var Reader = (ISaveDataRequestReader)kernel.GetService(typeof(ISaveDataRequestReader));
var Writer = (ISaveDataResponseWriter)kernel.GetService(typeof(ISaveDataResponseWriter));
var DumpService = (IDumpService)kernel.GetService(typeof(IDumpService));
// At this point, three instances of LogService have been created.
}
}
}
Edit: I've created a fresh minimal ASP.NET Web Forms project that reproduces the problem which you can download here but all the essential elements are already described in the code above.
Thanks.
If I try to use unity interception within a asp.net-core webapi my call-handlers are never getting called. I know it works in .net-core console applications, in non core applications and even tests in the same project that do not have the webapi stuff around (resolving services from a test container).
Here is the (relevant) code I use to setup the api:
In Program.cs:
.UseUnityServiceProvider()
In Startup.cs
public void ConfigureContainer(IUnityContainer container) {
container.AddNewExtension<Interception>();
container.RegisterType<IProjectService, ProjectService>().Configure<Interception>().SetInterceptorFor<IProjectService>(new InterfaceInterceptor());
}
public void ConfigureServices(IServiceCollection services) {
...
services.AddControllersAsServices();
...
}
I suspect it has something to do with the Unity.Microsoft.DependencyInjection.ServiceProvider set by UseUnityServiceProvider() resolving the types in a way interception is not kicking in.
So what do I need to do to get interception working as it should be? Or is this a known bug somewhere?
[Update]
#Albert: Do you mean something like this:
container.RegisterType<IProjectService, ProjectService>(new InterceptionBehavior<PolicyInjectionBehavior>()); // new Interceptor<InterfaceInterceptor>()
Even when I add the InterfaceInterceptor as second parameter it does not change anything.
Some more info on the HandlerAttribute and CallHandler I use for testing purposes:
public class DummyLoggingAttribute : HandlerAttribute {
public override ICallHandler CreateHandler(IUnityContainer container)
return new DummyLoggerCallHandler(){Order = 2};
}
}
public class DummyLoggerCallHandler : ICallHandler{
private int _order;
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) {
Console.WriteLine("log vor ");
IMethodReturn result = getNext()(input, getNext);
Console.WriteLine("log danach ");
return result;
}
public int Order {
get { return _order; }
set { _order = value; }
}
}
Applied to a method as follows.
[DummyLogging]
public virtual IList<Project> GetAllProjects() {
return _projectDao.GetAll();
}
It even does not make any differences if I add the Attribute to the interface, the implementation or both.
I another .net-core console application I can even successfully use the following extension to automatically configure each registering type for interception.
/// <summary>
/// Extension für das automatische Intercepten von Methoden
/// </summary>
public class InterceptionExtension : UnityContainerExtension {
/// <summary>
/// Entfernt die Extension
/// </summary>
public override void Remove() {
Context.Registering -= OnRegister;
Context.RegisteringInstance -= OnRegisterInstance;
}
/// <summary>
/// Initialisiert die Extension
/// </summary>
protected override void Initialize() {
Container.AddNewExtension<Interception>();
Context.Registering += OnRegister;
Context.RegisteringInstance += OnRegisterInstance;
}
/// <summary>
/// Event wenn ein Type Registriert wird
/// </summary>
private void OnRegister(object sender, RegisterEventArgs e) {
if (e.TypeFrom != null) {
Container.Configure<Interception>()
.SetInterceptorFor(e.TypeFrom, new InterfaceInterceptor());
}
}
/// <summary>
/// Event wenn eine Instance Regestriert wird
/// </summary>
private void OnRegisterInstance(object sender, RegisterInstanceEventArgs e) {
Container.Configure<Interception>()
.SetInterceptorFor(e.RegisteredType, new InterfaceInterceptor());
}
}
Which leads me to the assumption that it is not a problem with registering interception but resolving services through UnityServiceProvider.
I'm not sure that this is an answer to the question.
As I understand the original issue is that the proxy class that must intercept method calls of ProjectService is not being created.
I was able to fix it by creation a class derived from IInterceptionBehavior and a little bit different type registration.
MyInterceptionBehavior:
public class MyInterceptionBehavior : IInterceptionBehavior
{
public bool WillExecute
{
get { return true; }
}
public IEnumerable<Type> GetRequiredInterfaces()
{
return Enumerable.Empty<Type>();
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
IMethodReturn result = getNext()(input, getNext);
Console.WriteLine("Interception Called");
return result;
}
}
And in ConfigureContainer() replace your IProjectService registration with this:
container.RegisterType<IProjectService, ProjectService>(
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<MyInterceptionBehavior>());
Invoke method is called every time when every method of IProjectService is called. I hope this will give you some idea or helps to make work you interception behavior.
I have a case in ASP.NET,that when a request got, a web page (naming it as service.aspx)will be called asynchronously, and I don't care the response of service.aspx, I just need call it .
Right now ,I have two ways.
First one is HttpWebRequest.BeginGetResponse, parameter callback is set as null. And it seems that it's not a good idea , there is no way to call EndGetResponse, so maybe the resource of calling service.aspx thread could not be released.
Second one is WebClient.OpenReadAsync, but I'm not sure whether it could release thread resource if I don't specify OpenReadCompleted event.
Or maybe there is other appropriate way to have what I want.
You can create a class that will make web requests in the background. Create a static instance of this class in your application's HttpApplication class (Global.asax) and then call a method to queue web requests as required.
Class that performs web requests in the background
public class SiteBackgroundCaller : IRegisteredObject, IDisposable
{
private BlockingCollection<string> requestList;
private CancellationTokenSource queueWorkerCts;
private Task queueWorkerThread;
public SiteBackgroundCaller()
{
// Register an instance of this class with the hosting environment, so we can terminate the task gracefully.
HostingEnvironment.RegisterObject(this);
requestList = new BlockingCollection<string>();
queueWorkerCts = new CancellationTokenSource();
queueWorkerThread = new Task(queueWorkerMethod, TaskCreationOptions.LongRunning);
queueWorkerThread.Start();
}
public void QueueBackgroundRequest(string uri)
{
requestList.Add(uri);
}
private void queueWorkerMethod()
{
while (!queueWorkerCts.IsCancellationRequested)
{
try
{
// This line will block until there is something in the collection
string uri = requestList.Take(queueWorkerCts.Token);
if (queueWorkerCts.IsCancellationRequested)
return;
// Make the request
HttpWebRequest r = (HttpWebRequest)HttpWebRequest.Create(uri);
HttpWebResponse response = (HttpWebResponse)r.GetResponse();
}
catch (OperationCanceledException)
{
// This may throw if the cancellation token is Cancelled.
}
catch (WebException)
{
// Something wrong with the web request (eg timeout)
}
}
}
// Implement IRegisteredObject
public void Stop(bool immediate)
{
queueWorkerCts.Cancel();
queueWorkerThread.Wait();
}
// Implement IDisposable
public void Dispose()
{
HostingEnvironment.UnregisterObject(this);
}
}
Instance of the class in HttpApplication (in Global.asax)
public class Global : System.Web.HttpApplication
{
public static SiteBackgroundCaller BackgroundCaller { get; private set; }
protected void Application_Start(object sender, EventArgs e)
{
BackgroundCaller = new SiteBackgroundCaller();
}
}
Queuing a web request from a page
public partial class MyPage: System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
Global.BackgroundCaller.QueueBackgroundRequest("http://www.example.com/service.aspx");
}
}
You can create the HttpWebRequest and not read the response:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("service.aspx");
using (var response = request.GetResponse())
{
// typically here you would call GetResponseStream and read the content
}
you can also use the async variation:
using (var response = await request.GetResponseAsync())
{
}
In my program I'm trying to use session variable in IHttpModule. Here is my code. This is working fine in VS 2010 development server. But when I try to debug in IIS7 it shows exception System.Web.HttpException: Session state is not available in this context
So why session not available in IIS 7 but in development server.
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
HttpApplication http_application = sender as HttpApplication;
http_application.Session["language"] = "test";
}
}
All you need is to implement IRequiresSessionState.
So your code should look like:
public class Globalizer : IHttpModule, IRequiresSessionState
if all you use is reading Sessions (and never update them) then you should use IReadOnlySessionState as this last do not lock the session state, and therefor, you will not have concurrent requests).
If possible, move your handler to a PostAcquireRequestState handler:
Occurs when the request state (for example, session state) that is associated with the current request has been obtained.
(Or an even later event)
The SessionStateModule itself loads session state in response to the AcquireRequestState event - So at the moment, you're depending on whether your module or the session state module's handlers fire first.
Found this over on the ASP.NET forums:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regradless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
Found the reason.
If is because AcquireRequestState trigger for the all files such as CSS, JS, images. those files are not having session.
solution: Seems there is a way to avoid calling IHttpModule for all the request. ckeck this answer JS,Images and CSS getting intercepted by HTTPModule.
But it didn't work for me. So I uses HttpContext.Current.Session instead of HttpApplication.Session and every time it checks if Session is null before save to session.
If some one knows, How to avoid calling IHttpModule for files other than .aspx Please help here.
Here is the final code
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
if(HttpContext.Current.Session != null){
HttpContext.Current.Session["language"] = "test";
}
}
}
Edit: One other way is only use session if request comes to a .aspx file
HttpApplication http_application = sender as HttpApplication;
HttpContext context = http_application.Context;
if(Path.GetExtension(context.Request.PhysicalPath) == ".aspx")
{
HttpContext.Current.Session["language"] = "test";
http_application.Session["language2"] = "test2";
}
I'm struggling with trying to find the best way to implement WCF retries. I'm hoping to make the client experience as clean as possible. There are two approaches of which I'm aware (see below). My question is: "Is there a third approach that I'm missing? Maybe one that's the generally accepted way of doing this?"
Approach #1: Create a proxy that implements the service interface. For each call to the proxy, implement retries.
public class Proxy : ISomeWcfServiceInterface
{
public int Foo(int snurl)
{
return MakeWcfCall<int>(() => _channel.Foo(snurl));
}
public string Bar(string snuh)
{
return MakeWcfCall<string>(() => _channel.Bar(snuh));
}
private static T MakeWcfCall<T>(Func<T> func)
{
// Invoke the func and implement retries.
}
}
Approach #2: Change MakeWcfCall() (above) to public, and have the consuming code send the func directly.
What I don't like about approach #1 is having to update the Proxy class every time the interface changes.
What I don't like about approach #2 is the client having to wrap their call in a func.
Am I missing a better way?
EDIT
I've posted an answer here (see below), based on the accepted answer that pointed me in the right direction. I thought I'd share my code, in an answer, to help someone work through what I had to work through. Hope it helps.
I have done this very type of thing and I solved this problem using .net's RealProxy class.
Using RealProxy, you can create an actual proxy at runtime using your provided interface. From the calling code it is as if they are using an IFoo channel, but in fact it is a IFoo to the proxy and then you get a chance to intercept the calls to any method, property, constructors, etc…
Deriving from RealProxy, you can then override the Invoke method to intercept calls to the WCF methods and handle CommunicationException, etc.
Note: This shouldn't be the accepted answer, but I wanted to post the solution in case it helps others. Jim's answer pointed me in this direction.
First, the consuming code, showing how it works:
static void Main(string[] args)
{
var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding());
var endpointAddress = ConfigurationManager.AppSettings["endpointAddress"];
// The call to CreateChannel() actually returns a proxy that can intercept calls to the
// service. This is done so that the proxy can retry on communication failures.
IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));
Console.WriteLine("Enter some information to echo to the Presto service:");
string message = Console.ReadLine();
string returnMessage = prestoService.Echo(message);
Console.WriteLine("Presto responds: {0}", returnMessage);
Console.WriteLine("Press any key to stop the program.");
Console.ReadKey();
}
The WcfChannelFactory:
public class WcfChannelFactory<T> : ChannelFactory<T> where T : class
{
public WcfChannelFactory(Binding binding) : base(binding) {}
public T CreateBaseChannel()
{
return base.CreateChannel(this.Endpoint.Address, null);
}
public override T CreateChannel(EndpointAddress address, Uri via)
{
// This is where the magic happens. We don't really return a channel here;
// we return WcfClientProxy.GetTransparentProxy(). That class will now
// have the chance to intercept calls to the service.
this.Endpoint.Address = address;
var proxy = new WcfClientProxy<T>(this);
return proxy.GetTransparentProxy() as T;
}
}
The WcfClientProxy: (This is where we intercept and retry.)
public class WcfClientProxy<T> : RealProxy where T : class
{
private WcfChannelFactory<T> _channelFactory;
public WcfClientProxy(WcfChannelFactory<T> channelFactory) : base(typeof(T))
{
this._channelFactory = channelFactory;
}
public override IMessage Invoke(IMessage msg)
{
// When a service method gets called, we intercept it here and call it below with methodBase.Invoke().
var methodCall = msg as IMethodCallMessage;
var methodBase = methodCall.MethodBase;
// We can't call CreateChannel() because that creates an instance of this class,
// and we'd end up with a stack overflow. So, call CreateBaseChannel() to get the
// actual service.
T wcfService = this._channelFactory.CreateBaseChannel();
try
{
var result = methodBase.Invoke(wcfService, methodCall.Args);
return new ReturnMessage(
result, // Operation result
null, // Out arguments
0, // Out arguments count
methodCall.LogicalCallContext, // Call context
methodCall); // Original message
}
catch (FaultException)
{
// Need to specifically catch and rethrow FaultExceptions to bypass the CommunicationException catch.
// This is needed to distinguish between Faults and underlying communication exceptions.
throw;
}
catch (CommunicationException ex)
{
// Handle CommunicationException and implement retries here.
throw new NotImplementedException();
}
}
}
Sequence diagram of a call being intercepted by the proxy:
You can implement generic proxy for example using Castle. There is a good article here http://www.planetgeek.ch/2010/10/13/dynamic-proxy-for-wcf-with-castle-dynamicproxy/. This approach will give user object which implements interface and you will have one class responsible for comunication
Do not mess with generated code because, as you mentioned, it will be generated again so any customization will be overridden.
I see two ways:
Write/generate a partial class for each proxy that contains the retry variation. This is messy because you will still have to adjust it when the proxy changes
Make a custom version of svcutil that allows you to generate a proxy that derives from a different base class and put the retry code in that base class. This is quite some work but it can be done and solves the issue in a robust way.
go through approach 1, then wrape all context event (open, opened, faulted, ...) into event to be exposed by your class proxy, once the communication is faulted then re-create the proxy or call some recursive method inside proxy class. i can share with you some wok i have just tested.
public class DuplexCallBackNotificationIntegrationExtension : IExtension, INotificationPusherCallback {
#region - Field(s) -
private static Timer _Timer = null;
private static readonly object m_SyncRoot = new Object();
private static readonly Guid CMESchedulerApplicationID = Guid.NewGuid();
private static CancellationTokenSource cTokenSource = new CancellationTokenSource();
private static CancellationToken cToken = cTokenSource.Token;
#endregion
#region - Event(s) -
/// <summary>
/// Event fired during Duplex callback.
/// </summary>
public static event EventHandler<CallBackEventArgs> CallBackEvent;
public static event EventHandler<System.EventArgs> InstanceContextOpeningEvent;
public static event EventHandler<System.EventArgs> InstanceContextOpenedEvent;
public static event EventHandler<System.EventArgs> InstanceContextClosingEvent;
public static event EventHandler<System.EventArgs> InstanceContextClosedEvent;
public static event EventHandler<System.EventArgs> InstanceContextFaultedEvent;
#endregion
#region - Property(ies) -
/// <summary>
/// Interface extension designation.
/// </summary>
public string Name {
get {
return "Duplex Call Back Notification Integration Extension.";
}
}
/// <summary>
/// GUI Interface extension.
/// </summary>
public IUIExtension UIExtension {
get {
return null;
}
}
#endregion
#region - Constructor(s) / Finalizer(s) -
/// <summary>
/// Initializes a new instance of the DuplexCallBackNotificationIntegrationExtension class.
/// </summary>
public DuplexCallBackNotificationIntegrationExtension() {
CallDuplexNotificationPusher();
}
#endregion
#region - Delegate Invoker(s) -
void ICommunicationObject_Opening(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Info("context_Opening");
this.OnInstanceContextOpening(e);
}
void ICommunicationObject_Opened(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Debug("context_Opened");
this.OnInstanceContextOpened(e);
}
void ICommunicationObject_Closing(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Debug("context_Closing");
this.OnInstanceContextClosing(e);
}
void ICommunicationObject_Closed(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Debug("context_Closed");
this.OnInstanceContextClosed(e);
}
void ICommunicationObject_Faulted(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Error("context_Faulted");
this.OnInstanceContextFaulted(e);
if (_Timer != null) {
_Timer.Dispose();
}
IChannel channel = sender as IChannel;
if (channel != null) {
channel.Abort();
channel.Close();
}
DoWorkRoutine(cToken);
}
protected virtual void OnCallBackEvent(Notification objNotification) {
if (CallBackEvent != null) {
CallBackEvent(this, new CallBackEventArgs(objNotification));
}
}
protected virtual void OnInstanceContextOpening(System.EventArgs e) {
if (InstanceContextOpeningEvent != null) {
InstanceContextOpeningEvent(this, e);
}
}
protected virtual void OnInstanceContextOpened(System.EventArgs e) {
if (InstanceContextOpenedEvent != null) {
InstanceContextOpenedEvent(this, e);
}
}
protected virtual void OnInstanceContextClosing(System.EventArgs e) {
if (InstanceContextClosingEvent != null) {
InstanceContextClosingEvent(this, e);
}
}
protected virtual void OnInstanceContextClosed(System.EventArgs e) {
if (InstanceContextClosedEvent != null) {
InstanceContextClosedEvent(this, e);
}
}
protected virtual void OnInstanceContextFaulted(System.EventArgs e) {
if (InstanceContextFaultedEvent != null) {
InstanceContextFaultedEvent(this, e);
}
}
#endregion
#region - IDisposable Member(s) -
#endregion
#region - Private Method(s) -
/// <summary>
///
/// </summary>
void CallDuplexNotificationPusher() {
var routine = Task.Factory.StartNew(() => DoWorkRoutine(cToken), cToken);
cToken.Register(() => cancelNotification());
}
/// <summary>
///
/// </summary>
/// <param name="ct"></param>
void DoWorkRoutine(CancellationToken ct) {
lock (m_SyncRoot) {
var context = new InstanceContext(this);
var proxy = new NotificationPusherClient(context);
ICommunicationObject communicationObject = proxy as ICommunicationObject;
communicationObject.Opening += new System.EventHandler(ICommunicationObject_Opening);
communicationObject.Opened += new System.EventHandler(ICommunicationObject_Opened);
communicationObject.Faulted += new System.EventHandler(ICommunicationObject_Faulted);
communicationObject.Closed += new System.EventHandler(ICommunicationObject_Closed);
communicationObject.Closing += new System.EventHandler(ICommunicationObject_Closing);
try {
proxy.Subscribe(CMESchedulerApplicationID.ToString());
}
catch (Exception ex) {
Logger.HELogger.DefaultLogger.DUPLEXLogger.Error(ex);
switch (communicationObject.State) {
case CommunicationState.Faulted:
proxy.Close();
break;
default:
break;
}
cTokenSource.Cancel();
cTokenSource.Dispose();
cTokenSource = new CancellationTokenSource();
cToken = cTokenSource.Token;
CallDuplexNotificationPusher();
}
bool KeepAliveCallBackEnabled = Properties.Settings.Default.KeepAliveCallBackEnabled;
if (KeepAliveCallBackEnabled) {
_Timer = new Timer(new TimerCallback(delegate(object item) {
DefaultLogger.DUPLEXLogger.Debug(string.Format("._._._._._. New Iteration {0: yyyy MM dd hh mm ss ffff} ._._._._._.", DateTime.Now.ToUniversalTime().ToString()));
DBNotificationPusherService.Acknowledgment reply = DBNotificationPusherService.Acknowledgment.NAK;
try {
reply = proxy.KeepAlive();
}
catch (Exception ex) {
DefaultLogger.DUPLEXLogger.Error(ex);
switch (communicationObject.State) {
case CommunicationState.Faulted:
case CommunicationState.Closed:
proxy.Abort();
ICommunicationObject_Faulted(null, null);
break;
default:
break;
}
}
DefaultLogger.DUPLEXLogger.Debug(string.Format("Acknowledgment = {0}.", reply.ToString()));
_Timer.Change(Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);
}), null, Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);
}
}
}
/// <summary>
///
/// </summary>
void cancelNotification() {
DefaultLogger.DUPLEXLogger.Warn("Cancellation request made!!");
}
#endregion
#region - Public Method(s) -
/// <summary>
/// Fire OnCallBackEvent event and fill automatic-recording collection with newest
/// </summary>
/// <param name="action"></param>
public void SendNotification(Notification objNotification) {
// Fire event callback.
OnCallBackEvent(objNotification);
}
#endregion
#region - Callback(s) -
private void OnAsyncExecutionComplete(IAsyncResult result) {
}
#endregion
}
Just wrap all service calls in a function, taking a delegate that would execute the passed delegate the amount of time necessary
internal R ExecuteServiceMethod<I, R>(Func<I, R> serviceCall, string userName, string password) {
//Note all clients have the name Manager, but this isn't a problem as they get resolved
//by type
ChannelFactory<I> factory = new ChannelFactory<I>("Manager");
factory.Credentials.UserName.UserName = userName;
factory.Credentials.UserName.Password = password;
I manager = factory.CreateChannel();
//Wrap below in a retry loop
return serviceCall.Invoke(manager);
}