Using PerWcfSession lifestyle with Castle WCF Integration Facility - c#

The following code uses the Castle Windsor 3.0's WCF Integration Facility to register a WCF self-hosted service:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
namespace SelfHost
{
[ServiceContract]
public interface IHelloWorldService
{
[OperationContract]
string SayHello(string name);
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class HelloWorldService : IHelloWorldService
{
private readonly PerSession _perSession;
public HelloWorldService(PerSession perSession)
{
_perSession = perSession;
}
public string SayHello(string name)
{
return string.Format("Hello, {0} {1}", name, _perSession.Info());
}
}
public class PerSession
{
private readonly string _now;
public PerSession()
{
_now = DateTime.Now.ToString();
}
public string Info()
{
return _now;
}
}
internal class Program
{
private static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/hello");
var container = new WindsorContainer();
container.AddFacility<WcfFacility>();
container.Register(
Component.For<PerSession>().LifeStyle.PerWcfSession(),
Component.For<IHelloWorldService>()
.ImplementedBy<HelloWorldService>()
.AsWcfService(
new DefaultServiceModel()
.AddBaseAddresses(baseAddress)
.AddEndpoints(WcfEndpoint.BoundTo(new BasicHttpBinding()).At("basic"))
.PublishMetadata(o => o.EnableHttpGet())
)
);
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
}
}
}
Trying to invoke the SayHello method using WcfTestClient.exe results in the following error:
Could not obtain scope for component SelfHost.PerSession. This is most
likely either a bug in custom IScopeAccessor or you're trying to
access scoped component outside of the scope (like a per-web-request
component outside of web request etc)
What is the correct way to use PerWcfSession components?

So I was missing a few things:
The ServiceContract needs to set the SessionMode property
[ServiceContract(SessionMode = SessionMode.Required)]
Likewise the ServiceBehavior needs to set the InstanceContextMode
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerSession)]
Finally, the service registration needs to change the Lifestyle from the default (Singleton) so that it gets recreated for each request (and the dependencies are re-evaluated) - Transient or PerWcfSession would work.
Also, because we require a session, the binding needs to change from the basicHttpBinding to something that that supports sessions:
Component.For<IHelloWorldService>()
.ImplementedBy<HelloWorldService>()
.LifestyleTransient()
.AsWcfService(
new DefaultServiceModel()
.AddBaseAddresses(baseAddress)
.AddEndpoints(WcfEndpoint.BoundTo(new WSHttpBinding()).At("myBinding"))
.PublishMetadata(o => o.EnableHttpGet())
)
Which makes the final code look like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
namespace SelfHost
{
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IHelloWorldService
{
[OperationContract]
string SayHello(string name);
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerSession)]
public class HelloWorldService : IHelloWorldService
{
private readonly PerSession _perSession;
public HelloWorldService(PerSession perSession)
{
_perSession = perSession;
}
public string SayHello(string name)
{
return string.Format("Hello, {0} {1}", name, _perSession.Info());
}
}
public class PerSession
{
private readonly string _now;
public PerSession()
{
_now = DateTime.Now.ToString();
}
public string Info()
{
return _now;
}
}
internal class Program
{
private static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/hello");
var container = new WindsorContainer();
container.AddFacility<WcfFacility>();
container.Register(
Component.For<PerSession>().LifeStyle.PerWebRequest,
Component.For<IHelloWorldService>()
.ImplementedBy<HelloWorldService>()
.LifeStyle.PerWebRequest
.AsWcfService(
new DefaultServiceModel()
.AddBaseAddresses(baseAddress)
.AddEndpoints(WcfEndpoint.BoundTo(new WSHttpBinding()).At("myBinding"))
.PublishMetadata(o => o.EnableHttpGet())
)
);
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
}
}
}

Related

Host C# rest service - Error on return type

I'm trying to host a Rest service i created in c#, deploying a .dll file and importing it in a new project with main method.
To create an host in the main I created an interface for my Rest service, but after the declaration of this interface i keep getting the error
"The name Content does not exist in the current context"
. Content is the return of my method, that should return only void or Task or Task T> because is async method.
How should I resolve this error?
This is part of the controller(Rest Service):
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.ServiceModel;
using Couchbase;
using Couchbase.Core;
using Couchbase.IO;
using JWT;
[ServiceContract]
public interface ApiController
{
Task<IHttpActionResult> SignUp(LoginModel model);
}
namespace Nerder_Backend.Controllers
{
[RoutePrefix("api/user")]
public class UserController : ApiController
{
private readonly IBucket _bucket = ClusterHelper.GetBucket(ConfigurationManager.AppSettings.Get("CouchbaseUserBucket"));
private readonly string _secretKey = ConfigurationManager.AppSettings["JWTTokenSecret"];
[Route("signup")]
[HttpPost]
public async Task<IHttpActionResult> SignUp(LoginModel model)
{
if (model == null || !model.IsValid())
{
return Content(HttpStatusCode.BadRequest, new Error("Invalid email and/or password"));
}
var userKey = CreateUserKey(model.Email);
if (await _bucket.ExistsAsync(userKey))
{
return Content(HttpStatusCode.Conflict, new Error($"email '{model.Email}' already exists"));
}
var userDoc = new Document<User>
{
Id = userKey,
Content = new User
{
Email = model.Email,
Password = CalcuateMd5Hash(model.Password)
},
Expiry = model.Expiry
};
var result = await _bucket.InsertAsync(userDoc);
if (!result.Success)
{
return Content(HttpStatusCode.InternalServerError, new Error(result.Message));
}
var data = new
{
token = BuildToken(model.Email)
};
var context = $"Created user with ID '{userKey}' in bucket '{_bucket.Name}' that expires in {userDoc.Expiry}ms";
return Content(HttpStatusCode.Accepted, new Result(data, context));
}
This is the main method :
namespace MockServer
{
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/signup");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(UserController), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
}
Update main to host the web api properly.
include the following using statements
using System.Web.Http;
using System.Web.Http.SelfHost; //install the nuget package Microsoft.AspNet.WebApi.SelfHost
update Program class to Host the Web API
class Program {
static void Main(string[] args) {
var baseAddress = "http://localhost:8080";
var config = new HttpSelfHostConfiguration(baseAddress);
config.MapHttpAttributeRoutes();//map attribute routes
// Create the server
using (var server = new HttpSelfHostServer(config)) {
server.OpenAsync().Wait();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
}
}
}
Now make sure the api controller it correctly defined.
[RoutePrefix("api/user")]
public class UserController : ApiController {
private readonly IBucket _bucket = ClusterHelper.GetBucket(ConfigurationManager.AppSettings.Get("CouchbaseUserBucket"));
private readonly string _secretKey = ConfigurationManager.AppSettings["JWTTokenSecret"];
[HttpPost]
[Route("signup")] //Matches POST api/user/signup
public async Task<IHttpActionResult> SignUp(LoginModel model) {
//...code removed for brevity
}
}
Given the program set and controller attribute routes, the action would be found at
POST http://localhost:8080/api/user/signup

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();
...
}

WebService based on an interface for mocking

Question background:
I have two separate projects. One consists of class and a test project, the other a web service. I want to implement mocking against the web service.
Question:
I have a web reference to the web service called 'webService' being consumed by the class 'ProxyHandler', as shown:
private webService _webServiceObject
public ProxyHandler(webService webServiceObject)
{
_webService = webServiceObject;
}
The web service class implements an interface, as shown
public class WebServiceClass:IWebService
Which Implementation?:
Can I get the web reference to be of the type 'IWebService'?
Or, do I need to simply implement a new class based on an interface that consumes the webservice, then this class itself is implemented by the proxy? This would then allow me to mock against the interface, as shown:
Modified Proxy class:
private webService _webServiceHandlerObject;
public ProxyHandler(IwebServiceHandler webServiceHandlerObject)
{
_webServiceHandlerObject = webServiceHandlerObject;
}
Added 'WebServiceHandlerObject:
private webService _webServiceObject;
public Class WebServiceHandler:IwebServiceHandler
public WebServiceHandler(webService webServiceObject)
{
_webServiceObject = webServiceObject;
}
To give:
var proxy = new ProxyHandler(new WebServiceHandler(new webService()));
var mockedProxy = newMock<IwebServiceHandler>();
You abolutely can. Below is an example that I created using a service reference created in Visual Studio. In this example USZipSoap is the web service interface and USZipSoapClient is the implementing class. This all comes out of the box. Often you will need to do some configuration to your service, so below demonstrates how to use a factory method with Unity to resolve your dependencies.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ClassLibrary3.MyService;
using Microsoft.Practices.Unity;
namespace ClassLibrary3
{
public class ProxyHandler
{
public USZipSoap iwebService { get; set; }
public ProxyHandler(USZipSoap iWebService)
{
this.iwebService = iWebService;
}
public string GetZipInfo()
{
return iwebService.GetInfoByZIP("20008").InnerXml;
}
public static IUnityContainer BootstrapContainer()
{
var container = new UnityContainer();
//Simple Registration
//container.RegisterType<USZipSoap, USZipSoapClient>("Simple", new InjectionConstructor(new object[0]));
//Factory registration
container.RegisterType<USZipSoap>(new InjectionFactory(c => ProxyHandler.CreateSoapClient()));
return container;
}
public static USZipSoap CreateSoapClient()
{
var client = new USZipSoapClient();
/*Configure your client */
return client;
}
public static void Main()
{
var container = ProxyHandler.BootstrapContainer();
var proxy2 = container.Resolve<USZipSoap>();
var proxy1 = container.Resolve<ProxyHandler>();
Console.WriteLine(proxy1.GetZipInfo());
Console.ReadLine();
}
}
}
Then your unit tests would look like this with Moq:
[global:Microsoft.VisualStudio.TestTools.UnitTesting.TestClass]
public class MyTestClass
{
[global::Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]
public void MyTestMethod()
{
//Arrange
var mock = new Mock<USZipSoap>();
var proxy = new ProxyHandler(mock.Object);
//Act
var result = proxy.GetZipInfo();
//Assert
mock.Verify(m => m.GetInfoByZIP("20008"), Times.Once, "Error");
}
}

Need server to call client function ( NO CALLBACK!)

I've been doing some research on this and I got nothing.
I have a server and client.
My client does request to the server and the server runs some callbacks.
This works fine.
But now, there are some functions from the clients I need to call from the server and are not a result of a client call so I can't use callbacks there.
I'm using WCF and .net 4.0
Any suggestions?
CLIENT:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.IO;
using System.Collections;
namespace WCFClient
{
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(ICallbacks))]
public interface IMessageHandler
{
[OperationContract]
void HandleMessage();
}
public interface ICallbacks
{
[OperationContract(IsOneWay = true)]
void QueuePaths_Callback(string cPath, string EPath, string RPath, string IPath, string OPath);
}
public class Callbacks : ICallbacks
{
public void QueuePaths_Callback(string cPath)
{
Console.WriteLine("QueuePaths_Callback: " + cPath);
}
}
class Program
{
static void Main(string[] args)
{
Callbacks myCallbacks = new Callbacks();
DuplexChannelFactory<IMessageHandler> pipeFactory =
new DuplexChannelFactory<IMessageHandler>(
myCallbacks,
new NetNamedPipeBinding(),
new EndpointAddress(
"net.pipe://localhost/PipeReverse"));
IMessageHandler pipeProxy =
pipeFactory.CreateChannel();
while (true)
{
string str = Console.ReadLine();
pipeProxy.HandleMessage();//send the type for example
}
}
public void IWANTTOCALLTHISFROMSERVER()
{ }
}
}
SERVER:
namespace WCFServer
{
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(ICallbacks))]
public interface IMessageHandler
{
[OperationContract]
void HandleMessage();
}
public interface ICallbacks
{
[OperationContract(IsOneWay = true)]
void QueuePaths_Callback(string cPath);
}
public class StringReverser : IMessageHandler
{
public void HandleMessage()//handle the type and do the request
{
ICallbacks callbacks = OperationContext.Current.GetCallbackChannel<ICallbacks>();
callbacks.QueuePaths_Callback("path1");
}
}
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(
typeof(StringReverser),
new Uri[]{
new Uri("net.pipe://localhost")
}))
{
host.AddServiceEndpoint(typeof(IMessageHandler),
new NetNamedPipeBinding(),
"PipeReverse");
host.
host.Open();
Console.WriteLine("Service is available. " +
"Press <ENTER> to exit.");
Console.ReadLine();
//BLA BLA BLA
//CALL IWANTTOCALLTHISFROMSERVER();
host.Close();
}
}
}
}
If you want to inform client that something happened on the server you're looking for Duplex Service.
In full .net you have 2 options for bindings:
netTcpBinding
wsDualHttpBinding
netTcpBinding is much better since it doesn't require the client to open port (wsDualHttpBinding does require it).
To be honest the best binding is PollingDuplexHttpBinding that is only available for silverlight. However, it is not that hard to emulate it using basicHttpBinding.
The topic is quite broad so I recommend further reading.

I get an InvalidOperationException When Invoking a Service

The contract type HelloIndigo.Service is not attributed with
ServiceContractAttribute. In order to define a valid contract, the
specified type (either contract interface or service class) must be
attributed with ServiceContractAttribute.
I build a library class and referenced to the class in the console application.
The library class:
namespace HelloIndigo
{
public class Service : IHelloIndigoService
{
public string HelloIndigo()
{
return "Hello Indigo";
}
}
[ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
interface IHelloIndigoService
{
[OperationContract]
string HelloIndigo();
}
}
The console application:
namespace Host
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.Service),
new Uri("http://localhost:8000/HelloIndigo")))
{
host.AddServiceEndpoint(typeof(HelloIndigo.Service),
new BasicHttpBinding(),"Service");
host.Open();
Console.WriteLine("Press enter to terminate the host service");
Console.ReadLine();
}
}
}
}
When you add the endpoint, you should supply the interface that is the contract:
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),
new BasicHttpBinding(),"Service");

Categories

Resources