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.
Related
I have a very basic self host WCF console application hosting a SOAP service. Unfortunately when I look at the response to a call to a method in the service, the serialized response is actually the serialized private variables rather than the public ones. The client making the calls doesn't expect this so doesn't see any valid values in the response. Code for the service is below:
using (ServiceHost host = new ServiceHost(typeof(MyService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.Open();
... ...
}
public class MyService: IMyService
{
public MyReturnType MyMethod()
{
return new MyReturnType();
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.2612.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")][System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aaa123.com/WebServices")]
public partial class MyReturnType {
private string myPropertyField;
public string MyProperty { get { return myPropertyField; } set { this.myPropertyField = value; } }
So in the call to MyMethod above I would see the property in the response XML defined as myPropertyField, rather than MyProperty.
Is there any way to change this behaviour?
It seems if I add the attribute below on the method declaration in the interface, the response is serialised correctly:
System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults = true)]
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
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();
I’ve created an object that I would like to pass in a WCF call… but inside ServiceReference1… this object is redefined… is there a way to just use the original object everywhere… it seems like people have done this but I can’t figure out what I am doing wrong.
The object is used as a parameter to a function in the service contract.
[OperationContract(IsOneWay = true)]
void UpdateInformation(MyObject myObject);
The error that I get when I try to call the function from my client is “Argument 1: cannot convert from ‘MyNameSpaceDTO.MyObject' to ‘MyNameSpace.ServiceReference1.MyObject’”
The object is in it’s own class library dll and it is marked with [DataObject] and [DataMember] attributes.
namespace MyNameSpaceDTO
{
[DataContract]
public class MyObject
{
[DataMember]
public string Name { get; set; }
….
But, also ends up in Reference.cs after adding the Service Reference as:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="MyObject", Namespace="http://schemas.datacontract.org/2004/07/MyNameSpaceDTO")]
[System.SerializableAttribute()]
public partial class MyObject : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string NameField;
...
Also, I do have the following set in the Advanced section of the Add Service Reference:
[x] Reuse types in referenced assemblies
(o) Reuse types in all referenced assemblies
For consuming a WCF service you often see samples (and they're undoubtedly advisable!) where you're instructed to add that service via the Add Service Reference dialog. By referencing a service that way your client application creates proxy classes form the WSDL exposed by the service.
As a result you end up having e.g. a class MyNameSpaceDTO.MyObject in your contract-assembly and a MyNameSpace.ServiceReference1.MyObject in your client application which was generated form the WSDL. This may seem somewhat redundant.
One situation in which you may need this behaviour could be the following: Imagine you'd want to consume an arbitrary public web service which you don't control. You have no access to the contract-assembly which defines the types etc. In that situation creating your own local proxy classes from the exposed WSDL is optimal since it's your only way to get the needed types and so on.
But your concrete situation seems to be a little bit different. I think what you're looking for is a shared contract. Since you're in control of the client and server code (and both live happily side by side in the same solution), you're in the comfortable situation to just share the contract:
So instead of adding a service reference within your client-app (via Add Service Reference), you'd just reference the contract-assembly (via the usual Add Reference dialogue). By doing this there'll by only one MyNameSpaceDTO.MyObject since the second one is never created and not needed. This approach is called contract sharing.
Please take a look at that example:
EDIT:
Please note some changes: The most important one is that you usually wouldn't want to share the assembly which holds your implementation logic of your service. So I extracted that part from the Contract-assembly and put it in a separate Implementation-assembly. By doing so, you simply share the interfaces and types and not the implementation logic. This change is reflected in the screenshot above, too.
You could set up that small solution with the following classes:
Contract - IService1.cs:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
Implementation - Service1.cs:
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
Host - Program.cs:
class Program
{
static void Main(string[] args)
{
var baseAddress = new Uri("http://localhost:8732/Design_Time_Addresses/Service1/");
using (var host = new ServiceHost(typeof(Service1), baseAddress))
{
// Enable metadata publishing.
var 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();
host.Close();
}
}
}
Client - Program.cs:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press <Enter> to proceed.");
Console.ReadLine();
var binding = new BasicHttpBinding();
var endpoint = new EndpointAddress("http://localhost:8732/Design_Time_Addresses/Service1/");
var channelFactory = new ChannelFactory<IService1>(binding, endpoint);
// Create a channel.
IService1 wcfClient1 = channelFactory.CreateChannel();
string s = wcfClient1.GetData(42);
Console.WriteLine(s);
((IClientChannel)wcfClient1).Close();
Console.WriteLine("Press <Enter> to quit the client.");
Console.ReadLine();
}
}
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.