Here is my situation:
I have a base class and 70 delivered class that are inherited from
this.
I am implementing WCF named pipelines and these 70 class will be the
clients.
Need asynchronously receive and send data
For third article i am using [CallbackBehaviorAttribute(ConcurrencyMode = ConcurrencyMode.Multiple)] attribute and it works when i insert it on a delivered class. But I should insert the attribute on base class and i won't spend my time to insert a new attribute or remove existing attribute on all delivered class. But when i inserted on the base class, it doesn't work asynchronously. Because CallbackBehaviorAttribute is not inherited attribute.
So how can i solve the problem that i mentioned above? I tried to create a new attribute that inherites from CallbackBehaviorAttribute, but it is sealed.
Client Code:
[CallbackBehaviorAttribute(ConcurrencyMode = ConcurrencyMode.Multiple)]
public abstract partial class BaseService : IDownloaderCallbackService
{
public void connect_server_manager()
{
string address = "net.pipe://localhost/servermanager/";
var factory = new DuplexChannelFactory<IDownloaderServiceContract>(new InstanceContext(this), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), new EndpointAddress(address));
server_manager = factory.CreateChannel();
logger.Info("Client Connected");
logger.Info(string.Format(server_manager.Ping(this.this_service_id, new byte[1] { 0012 })));
logger.Info(string.Format(server_manager.SetLastRequstTime(this.this_service_id, DateTime.Now)));
}
}
Server Code:
string address = "net.pipe://localhost/servermanager/";
serviceHost = new ServiceHost(service_manager);
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
serviceHost.AddServiceEndpoint(typeof(IDownloaderServiceContract), binding, address);
serviceHost.Open();
Console.WriteLine("ServiceHost running. Press Return to Exit");
IDownloaderServiceContract Code:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IDownloaderCallbackService))]
public interface IDownloaderServiceContract
{
[OperationContract]
string Ping(uint service_id, byte[] p);
[OperationContract]
string Pong(uint service_id, byte[] p);
[OperationContract]
dynamic SetLastRequstTime(uint service_id, dynamic data);
}
I solved the problem by setting ConcurrencyMode as True in connection code block.
string address = "net.pipe://localhost/servermanager/";
var factory = new DuplexChannelFactory<IDownloaderServiceContract>(new InstanceContext(this), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), new EndpointAddress(address));
((CallbackBehaviorAttribute)factory.Endpoint.EndpointBehaviors[typeof(CallbackBehaviorAttribute)]).ConcurrencyMode = ConcurrencyMode.Multiple;
server_manager = factory.CreateChannel();
Related
I am exploring bit more detail of WCF Instance context mode and Concurrency and get confused over managed threadID value for BasicHttpBinding and WSHttpBinding.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class Service2 : IService1
{
int i;
public string GetData(int value)
{
string output = string.Format("Service2 {0} , {1} , {2}",
DateTime.Now.ToString(),
i++,
System.Threading.Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(output);
System.Threading.Thread.Sleep(5000);
return output;
}
}
Sample Test
public class program
{
[STAThread]
public static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(Service2),
new Uri("http://localhost:9011"),
new Uri("net.tcp://localhost:9009"));
host.AddServiceEndpoint(typeof(IService1),
new BasicHttpBinding(), "");
host.AddServiceEndpoint(typeof(IService1),
new WSHttpBinding(), "WS");
Test1();
Console.WriteLine("-------------------------------");
Test2();
Console.WriteLine("-------------------------------");
Console.ReadLine();
}
public static void Test1()
{
EndpointAddress endPoint = new EndpointAddress("http://localhost:9011");
BasicHttpBinding binding = new BasicHttpBinding();
ChannelFactory<IService1> client = new ChannelFactory<IService1>(binding, endPoint);
IService1 proxy = client.CreateChannel();
Enumerable.Range(1, 10).ToList().ForEach(cc =>
{
proxy.GetData(10);
});
}
public static void Test2()
{
EndpointAddress endPoint = new EndpointAddress("http://localhost:9011/WS");
WSHttpBinding binding = new WSHttpBinding();
ChannelFactory<IService1> client = new ChannelFactory<IService1>(binding, endPoint);
IService1 proxy = client.CreateChannel();
Enumerable.Range(1, 10).ToList().ForEach(cc =>
{
proxy.GetData(10);
});
}
}
Now Problem is with ManagedThreadId.
If you look at output of Test1() then ManagedThreadId value is same for all 10.
But if you look at Test2() Then ManagedThreadId value it is different.
Why it is like that ?
InstanceContextMode and ConcurrencyMode are essentially an abstraction layer that controls the instance lifetime of a service object and how calls are serialised (if any) to it and does not describe threading models to the point of whether the same thread will be used or not.
If we look at your example:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
...that's essentially setting up thread-safe; singleton service. For single mode concurrency, all calls are queued and executed one at a time. 1
Custom Thread Synchonisation Contexts
In my readings and experience, though WCF doesn't say anything about which thread will be used for the above attributes, WCF is compatible with different and custom thread synchonisation contexts though such implementations have:
"...nothing to do with WCF"; Lowy, 1
OP:
If you look at output of Test1() then ManagedThreadId value is same for all 10.
But if you look at Test2() Then ManagedThreadId value it is different
I'd say all you are seeing there is perhaps the default service behavior under BasicHttpBinding is to use a custom thread pool synchoniser where calls are queued up and executed in turn on the same thread whereas under WSHttpBinding the default thread pool is used (hence why the threads are different).
More
Programming WCF Services: Mastering WCF and the Azure AppFabric Service Bus Third Edition
My code consumes a third party REST service using WCF. The service interface is declared like this:
[ServiceContract(Namespace = "SomeNamespace",
ConfigurationName = "SomeName")]
public interface ICoolService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = #"whatever")]
void CoolMethod(InputContainer input);
}
where InputContainer is declared as a DataContract:
[DataContract(Namespace = "whatever")]
public class InputContainer : IExtensibleDataObject
{
//[DataMember]s inside
}
My code instantiates uses WebChannelFactory to instantiate a "channel object" and then makes calls to the service via the "channel object"
ServiceEndpoint endpoint = ...craft endpoint;
var factory = new WebChannelFactory<IServiceManagement>( endpoint );
var service = factory.CreateChannel();
service.CoolMethod( new InputContainer() );
and it works quite well.
Now the problem... The documentation to that service says that the service returns a response with x-some-cool-header and empty body.
How do I obtain the value of that response header (preferably as a return value of CoolMethod())?
The simplest way is to change the interface declaration such that the method returns System.ServiceModel.Channels.Message:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = #"whatever")]
Message CoolMethod(InputContainer input);
then once method invokation is complete you get a Message object which contains the HTTP response with the headers:
var invokationResult = service.CoolMethod( new InputContainer() );
var properties = message.Properties;
var httpResponse =
(HttpResponseMessageProperty)properties[HttpResponseMessageProperty.Name];
var responseHeaders = httpResponse.Headers;
var coolHeader = reponseHeaders["x-some-cool-header"];
I have the following scenario: I implemented a Java WS running on JBoss 5.1 (with Seam 2.2.0.GA):
#Name("service")
#WebService(name = "Service", serviceName = "Service", targetNamespace = "http://app.service")
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
#Stateless
public class Service implements ServiceContract {
#Override
#WebMethod(operationName = "serviceOperation")
public OperationResponse serviceOperation(#WebParam(name = "queryType") QueryType queryType) {
this.log.info(queryType);
// Validate queryType is not null:
if (queryType == null) {
return new OperationResponse("queryType is null");
}
// ... elided
return new OperationResponse("Query OK");
}
}
#XmlType
public enum QueryType {
LOCAL,
REMOTE;
}
#XmlType(name = "operationResponse", propOrder = {"message"})
public class OperationResponse {
private String message;
public OperationResponse () {
}
// getters and setters
}
A Java client consumes it just fine:
public class ServiceClient {
public void consume() {
OperationResponse response = svc.serviceOperation(QueryType.LOCAL);
this.log.info("rcop = #0", response.getMessage());
}
}
The service prints:
INFO [Service] LOCAL
The client prints:
INFO [ServiceClient] Query OK
Nevertheless, if consumed from a C# client (generated with VS 2008), the Java WS gets queryType as null
INFO [Service]
even when the parameter is set:
Service svc = new Service();
serviceOperation svcParams = new serviceOperation();
svcParams.queryType = queryType.LOCAL;
operationResponse response = svc.serviceOperation(svcParams);
Console.WriteLine(response.#return.message);
The client prints:
queryType is null
What is the reason for the service getting a null instead of the value set by the C# client? I have already searched the web and found nothing related to this problem. Am I missing any annotations for the enum in the Java side? Or is it a problem with the client generated by VS? Thanks four your attention.
I came up with a solution that I don't really like, but it works. Instead of using the enum parameter, I changed the method's signature to
public OperationResponse serviceOperation(#WebParam(name = "queryType") String queryType)
where queryType must be one of "LOCAL" or "REMOTE", then I get the enum instance by using Enum#valueOf(String). I really needed the enum because later I added a abstract method to the enum class and each instance must implement a specific behavior.
I need to build a service that serves two interfaces. One interface uses basicHttpBinding, and the other should be netTcpBinding. The other one should also support duplex communication.
basicHttp interface:
[ServiceContract(Name = "accesspointService")]
[XmlSerializerFormat]
public interface IVERAAccessPoint
{
[OperationContract]
CompositeType GetDataUsingDataContract(MyClass obj);
}
Implementation:
[ServiceBehavior(Name = "accesspointService", Namespace = "http://www.w3.org/2009/02/ws-tra")]
public class VERAAccessPoint : IVERAAccessPoint
{
public CompositeType GetDataUsingDataContract(MyClass obj)
{
//something
return composite;
}
}
duplex netTcpContract:
[ServiceContract(CallbackContract = typeof(IClientCallback))]
public interface IVERAAPCS
{
[OperationContract(IsOneWay=true)]
void Subscribe(ClientInfo info);
[OperationContract(IsOneWay=true)]
void Unsubscribe(ClientInfo info);
}
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void PushDocument(XDocument doc);
}
[DataContract]
public class ClientInfo
{
public string id;
}
And implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,ConcurrencyMode = ConcurrencyMode.Single)]
public class VERAAPCS : IVERAAPCS
{
public void Subscribe(ClientInfo info)
{
//something
}
public void Unsubscribe(ClientInfo info)
{
//Something
}
}
I tried to self host both interfaces and this is the best i could do:
Uri baseAddress1 = new Uri("http://localhost:6544/hello");
//host the first interface
using (ServiceHost host = new ServiceHost(typeof(VERAAccessPoint.VERAAccessPoint), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.Open();
//Host the second (duplex interface)
using (ServiceHost host2 = new ServiceHost(typeof(VERAAccessPoint.VERAAPCS)))
{
host2.AddServiceEndpoint(typeof(VERAAccessPoint.IVERAAPCS), new NetTcpBinding(), "net.tcp://localhost:6543/hello2");
host2.Open();
Console.ReadLine();
host2.Close();
}
host.Close();
}
Now for the consuming part:
//Consuming the first interface (this works so i removed it form the question)
//Consuming the second interface:
var myBinding = new NetTcpBinding();
var myEndpoint = new EndpointAddress("net.tcp://localhost:6543/hello2");
var myChannelFactory = new ChannelFactory<VERAAccessPoint.IVERAAPCS>(myBinding, myEndpoint);
VERAAccessPoint.IVERAAPCS client = null;
client = myChannelFactory.CreateChannel();
This produces the following error:
ChannelFactory does not support the contract IVERAAPCS as it defines a callback contract with one or more operations. Please consider using DuplexChannelFactory instead of ChannelFactory.
But I just can't seem to find a way to use the duplexChannelFactory.
So my question is basically how do you consume a duplex netTcpBinding service tat is self hosted?
Sorry for the long question, but I wanted to provide as much information as I could. Thanks
Per your request in the comments, here's an example.
Place all of your interfaces in a separate assembly. For purposes of this example, let's name it ServiceContracts and use the namespace VERAAccessPoint.ServiceContracts.
Inside this assembly (which you'll want to create as a class library - DLL), you place IVERAAccessPoint, IVERAAPCS, IClientCallback and the data contract ClientInfo.
Next, add add a reference to the ServiceContracts assembly in your self-hosted application and a using directive:
using VerAAccessPoint.ServiceContracts;
That way you can implement the contract interfaces and host the services.
Finally, in your client application add the reference to the assembly and the using directive, and then you can do the following:
IVERAAPCS client = null;
var myBinding = new NetTcpBinding();
var myEndpoint = new EndpointAddress("net.tcp://localhost:6543/hello2");
var myDuplexChannelFactory = new DuplexChannelFactory<IVERAAPCS>(myBinding, myEndpoint);
client = myDuplexChannelFactory.CreateChannel();
You could do something similar with ChannelFactory<T> using IVERAAccessPoint as well.
I have used ChannelFactory<T> a lot, but never the DuplexChannelFactory<T>, but this should give you another option to explore.
I'm new with WCF and still experimenting.
I have two ServiceContract like this:
[ServiceContract]
public interface IFirst
{
[OperationContract(IsOneWay = true)]
void First();
}
[ServiceContract]
public interface ISecond
{
[OperationContract(IsOneWay = true)]
void Second();
}
On server side Client class implementing those interfaces:
public class Client : IFirst, ISecond
{
static int count = 0;
int id;
public Client()
{
count++;
id = count;
Console.WriteLine("{0} client created.", id);
}
public void First()
{
Console.WriteLine("First from: " + id.ToString());
}
public void Second()
{
Console.WriteLine("Second: " + id.ToString());
}
}
and host:
ServiceHost host = new ServiceHost(typeof(Client), new Uri("net.tcp://localhost:8000"));
NetTcpBinding binding = new NetTcpBinding();
host.AddServiceEndpoint(typeof(IFirst), binding, "");
host.AddServiceEndpoint(typeof(ISecond), binding, "");
host.Open();
On client side:
ChannelFactory<IFirst> firstFactory = new ChannelFactory<IFirst>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
IFirst iFirst = firstFactory.CreateChannel();
iFirst.First();
ChannelFactory<ISecond> secondFactory = new ChannelFactory<ISecond>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
ISecond iSecond = secondFactory.CreateChannel();
iSecond.First();
This works fine. It calls First and then Second method but for each call creates new instance of Client class. It is obvious because when client creates channel, service creates new instance of Client object. What I wish to achieve is call Second after First but for the same instance of Client. Is it even possible? I know I can put this two methods in one service but it is not what I wish for.
As per my understanding you want to call the two methods while creating only one client.
In WCF you can control Instancing by setting InstanceContextMode property of Service Behavior Attribute. There are three possible values
-PerCall
-PerSession
-Single
You can use PerSession as it keeps the object active for the next calls from client.
The object is released when the session ends
You can decorate your class
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
Hope this helps.
At first I was a little confused by you calling your Service Implementation "Client" :P
But anyway, by default, WCF uses InstanceContextMode.PerCall, which means that it will instantiate a new Service implementation instance for every request into the Service.
If you want for subsequent service calls to be handled within the same service instance, you will have to use either PerSession or Single InstanceContextModes.
http://msdn.microsoft.com/en-us/library/ms731193.aspx summarises Sessions, Instances and Concurrency in WCF pretty well.