Wcf ChannelFactory - c#

I have a question about WCF and Channel Factory usage.
On Host :
[ServiceContract]
public interface IGetMessage
{
[OperationContract]
string ShowMessage(Sample p, string Username, string Password);
}
[DataContract]
public class Sample
{
[DataMember]
public string Name { get; set; }
}
public string ShowMessage(Sample p, string Username, string Password)
{
return p.Name.ToString() + " - " + "Correct"; //Error line
}
On Client :
[ServiceContract()]
public interface IGetMessage
{
[OperationContract()]
string ShowMessage(Sample p, string Username, string Password);
}
[DataContract()]
public class Sample
{
[DataMember]
public string Name { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
Sample p1 = new Sample();
p1.Name = "ALEX";
BasicHttpBinding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://MYURL/GetMessage.svc");
using (var myChannelFactory = new ChannelFactory<IGetMessage>(myBinding, myEndpoint))
{
IGetMessage client = null;
try
{
client = myChannelFactory.CreateChannel();
MessageBox.Show(client.ShowMessage(p1, "abc","123"));
((ICommunicationObject)client).Close();
myChannelFactory.Close();
}
catch
{
(client as ICommunicationObject)?.Abort();
}
}
}
If I add on client as Service Reference, it works perfectly. I can get "ALEX - Correct" message.
When I test on WcfTestClient.exe, it works perfectly.
But, I have a problem when using on Winform with above codes.
When I check on WcfServer Trace Log and Message Log;
System.NullReferenceException - Object reference not set to an instance of an object. Line number:22
Line number 22:
p.Name.ToString() on the Host's GetMessage.cvs.cs file.
I think, there is no problem on host. Problem is Client side.
I'd like to ask you how I made a mistake on the client side?
Regards.

As mentioned in the comments, to solve the problem you only to do is adding the namespace property to ServiceContract and DataContract. be sure that the namespace is identical between the client and the server. the reason why we should do is an error occurred during serialization and deserialization, we must ensure that the service contract and data contract has a consistent namespace between the server and client.
Here is my example(Add this feature on both the server and client.)
[ServiceContract(Namespace ="http://mydomain")]
public interface IGetMessage
{
[OperationContract]
string ShowMessage(Sample p, string Username, string Password);
}
[DataContract(Namespace = "http://mydomain")]
public class Sample
{
[DataMember]
public string Name { get; set; }
}
Result.
Feel free to let me know if there is anything I can help with.

Related

WCF service return base class only

I'm working on a WCF service and trying to cleanup code. Issue I'm running into is I have a base data type I want to send as results from the service to the client; however in the service code itself it will mostly make use of classes derived on the base with additional properties for processing. The client has no reason to know about these at all.
Right now I can get this working but only if I define the derived classes in the shared library. I do not want them in there as they are specific solely to the service.
Below is example to show the problem. All three files are in separate projects in the same solution.
Common.IPersonService.cs
using System.ServiceModel;
using System.Runtime.Serialization;
namespace Common
{
[ServiceContract]
public interface IPersonService
{
[OperationContract(Name = "GetPersonById")]
Person GetPersonById(int id);
}
[DataContract(Name = "Person")]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
}
}
WcfClient.Program.cs
using System;
using System.ServiceModel;
using Common;
namespace WcfClient
{
class Program
{
static void Main(string[] args)
{
var binding = new NetTcpBinding();
var endpoint = new EndpointAddress("net.tcp://localhost:8001/WcfTest/");
var factory = new ChannelFactory<IPersonService>(binding, endpoint);
IPersonService service = null;
try
{
service = factory.CreateChannel();
Person result = service.GetPersonById(5);
Console.WriteLine(result.Name);
((ICommunicationObject)service).Close();
Console.ReadLine();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
}
}
WcfService.Program.cs
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;
using Common;
namespace WcfService
{
[DataContract(Name = "Person")]
public class Contact : Person
{
public string Address { get; set; }
}
class Program
{
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(PersonService)))
{
serviceHost.Open();
Console.WriteLine("Service started");
Console.ReadLine();
}
}
}
public class PersonService : IPersonService
{
private Dictionary<int, Contact> _testData = new Dictionary<int, Contact>();
public PersonService()
{
Random rnd = new Random();
for (int i = 0; i < 100; i++)
{
_testData.Add(i + 1, new Contact()
{
Id = i + 1,
Name = Guid.NewGuid().ToString(),
Address = Guid.NewGuid().ToString()
});
}
}
public static void Configure(ServiceConfiguration config)
{
config.AddServiceEndpoint(typeof(IPersonService), new NetTcpBinding(), "net.tcp://localhost:8001/WcfTest/");
config.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
}
public Person GetPersonById(int id)
{
return _testData[id];
}
public Person GetValueByKey(string key)
{
return null;
}
}
}
The exception received is the following:
The socket connection was aborted. This could be caused by an error
processing your message or a receive timeout being exceeded by the
remote host, or an underlying network resource issue. Local socket
timeout was '00:00:59.9780000'.
Now if I move the Contact class from the WcfService project and put it in the Common project it will work. As said though I'd prefer not to muddy a common library with items specific to the service implementation.
Thanks!

WCF client and server need to translate class names/namespaces

Imagine a WCF service running on IIS. It has one method which returns one type:
namespace TheServer
{
[ServiceContract]
public interface IServerSideInterface
{
[OperationContract]
ServerSideResultType CreateParentData(ServerSideParameterType input);
}
}
However, on the client I wish to have:
namespace TheClient
{
[ServiceContract]
public interface IClientSideInterface
{
[OperationContract]
ClientSideResultType CreateParentData(ClientSideParameterType input);
}
}
It's actually slightly more complicated as I want it to be asynchronous, but one step at a time.
I wish to use a ChannelFactory to communicate from the client to the server.
It is here I am stuck.
The next bit of code uses the type names I'm using in my sample.
...
private readonly IClientWcfServiceChannel _client;
public ChanFacWcfServiceMainPageViewModel()
{
var f = new ChannelFactory<IClientWcfServiceChannel>(new BasicHttpBinding(),
new EndpointAddress("http://localhost:50001/WcfService.svc"));
_client = f.CreateChannel();
FireCommand = new RelayCommand(Execute);
}
private void Callback(IAsyncResult ar)
{
var result2 = _client.EndCreateParentData(ar);
//var result = ((IClientWcfService)ar.AsyncState).EndCreateParentData(ar);
Result = result2.ToString();
}
private void Execute()
{
_client.BeginCreateParentData(ClientWcfServiceStartUpMode.StartUpLater, Callback, SynchronizationContext.Current);
}
This gives me a "not found" exception in the Callback method.
How do I map from the server type to the client type? They are essentially identical except for the names. On the server everything starts "Server" and on the client, the types were copy and pasted and renamed with "Client" at the start. The namespace also.
I do not want to use a shared type in a library common to both projects and I do not want to use svcUtil or "Add service reference" to create proxies (although I have to poke at their code).
Additional info:
VS 2012 + 4.5.
Ok, so I've got it to work. There were a number of issues mixing together to make it more complicated than I expected. The end result uses a Silverlight client. Silverlight has some specific restrictions in that you have to use async calls for services. It throws an exception if you try to access a synchronous called service.
EDIT: Added some DataMember attributes.
So on the server I annotated the interfaces and classes:
[ServiceContract(Name = "MyServiceClass", Namespace = "Ian.Server")]
public interface IServerWcfService
{
[OperationContract]
ServerWcfServiceParentData CreateParentData(ServerWcfServiceStartUpMode mode);
}
[DataContract(Name = "ServiceChildData", Namespace = "Ian.Server")]
public class ServerWcfServiceParentData
{
[DataMember]
public IEnumerable<ServerWcfServiceChildData> Children { get; private set; }
}
[DataContract(Name = "ServiceChildData", Namespace = "Ian.Server")]
public class ServerWcfServiceChildData
{
[DataMember]
public string ChildName { get; set; }
[DataMember]
public ServerWcfServiceChildData NestedChild { get; set; }
[DataMember]
public string Text { get; set; }
}
[DataContract(Name = "ServiceStartUpMode", Namespace = "Ian.Server")]
public enum ServerWcfServiceStartUpMode
{
[EnumMember(Value = "None")]
None,
[EnumMember(Value = "StartUpNow")]
StartUpNow,
[EnumMember(Value = "StartUpLater")]
StartUpLater
}
On the client I created the same classes but with my new names and similar annotations:
[ServiceContract(Name = "MyServiceClass", Namespace = "Ian.Server")]
public interface IClientWcfService
{
[OperationContract(AsyncPattern = true, Action = "Ian.Server/MyServiceClass/CreateParentData",
ReplyAction = "Ian.Server/MyServiceClass/CreateParentDataResponse")]
IAsyncResult BeginCreateParentData(ClientWcfServiceStartUpMode mode, AsyncCallback callback, object asyncState);
ClientWcfServiceParentData EndCreateParentData(IAsyncResult result);
}
public interface IClientWcfServiceChannel : IClientWcfService, IClientChannel
{
}
[DataContract(Name = "ServiceChildData", Namespace = "Ian.Server")]
public class ClientWcfServiceParentData
{
[DataMember]
public IEnumerable<ClientWcfServiceChildData> Children { get; set; }
}
[DataContract(Name = "ServiceChildData", Namespace = "Ian.Server")]
public class ClientWcfServiceChildData
{
[DataMember]
public string ChildName { get; set; }
[DataMember]
public ClientWcfServiceChildData NestedChild { get; set; }
[DataMember]
public string Text { get; set; }
}
[DataContract(Name = "ServiceStartUpMode", Namespace = "Ian.Server")]
public enum ClientWcfServiceStartUpMode
{
[EnumMember(Value = "None")]
None,
[EnumMember(Value = "StartUpNow")]
StartUpNow,
[EnumMember(Value = "StartUpLater")]
StartUpLater
}
Notice the async changes for the service contract. We have a Begin and End pair with the async flag set. Only the Begin has an OperationContract attribute.
Also I set the Action and ReplyAction to the values I found in the wsdl.
I have a viewmodel in my Silverlight App, the important parts for calling the service are here:
private string _result;
private readonly IClientWcfServiceChannel _client;
public ChanFacWcfServiceMainPageViewModel()
{
var f = new ChannelFactory<IClientWcfServiceChannel>(new BasicHttpBinding(),
new EndpointAddress("http://localhost:50001/WcfService.svc"));
_client = f.CreateChannel();
FireCommand = new RelayCommand(Execute);
}
private void Callback(IAsyncResult ar)
{
var context = ar.AsyncState as SynchronizationContext;
if (context == null)
{
throw new Exception("wtf");
}
var result2 = _client.EndCreateParentData(ar);
context.Post(o => { Result = result2.ToString(); }, null);
}
private void Execute()
{
_client.BeginCreateParentData(ClientWcfServiceStartUpMode.StartUpLater, Callback, SynchronizationContext.Current);
}
It's quite scrappy still, passing the SyncContext around and so on, but it does work.
For this example it just returns the type name to the view, which is pointless but proves it has a) returned something and b) that the type is the type I expected.
Importantly there is no shared code. Nothing exists in a common Portable library for example.

Pass to my form WCF client message

I have Winforms application that host WCF service.
this is my Button connect event:
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
// Returns a list of ipaddress configuration
IPHostEntry ips = Dns.GetHostEntry(Dns.GetHostName());
// Get machine ipaddress
IPAddress _ipAddress = IPAddress.Parse(tbServerIp.Text);
// Create the url that is needed to specify where the service should be started
urlService = "net.tcp://" + _ipAddress.ToString() + ":8000/MyService";
// Instruct the ServiceHost that the type that is used is a ServiceLibrary.service1
host = new ServiceHost(typeof(ServiceLibrary.service1));
host.Opening += new EventHandler(host_Opening);
host.Opened += new EventHandler(host_Opened);
host.Closing += new EventHandler(host_Closing);
host.Closed += new EventHandler(host_Closed);
// The binding is where we can choose what transport layer we want to use. HTTP, TCP ect.
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None; // <- Very crucial
// Add a endpoint
host.AddServiceEndpoint(typeof(ServiceLibrary.IService1), tcpBinding, urlService);
// A channel to describe the service. Used with the proxy scvutil.exe tool
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
// This is how I create the proxy object that is generated via the svcutil.exe tool
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetUrl = new Uri("http://" + _ipAddress.ToString() + ":8001/MyService");
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.ToString();
host.Description.Behaviors.Add(metadataBehavior);
urlMeta = metadataBehavior.HttpGetUrl.ToString();
//pbIndicator.Image = Resources.indicator_green;
btnConnect.BackColor = Color.Red;
btnConnect.Text = "Stop";
}
host.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
And this is my ServiceLibrary:
namespace ServiceLibrary
{
public delegate void StatusEventHandler(string srt);
[ServiceContract()]
public interface IService1
{
[OperationContract]
string MyOperation1(string myValue);
[OperationContract]
string MyOperation2(DataContract1 dataContractValue);
[OperationContract]
string HelloWorld(string str);
}
public class service1 : IService1
{
public event StatusEventHandler StartEvent;
public string MyOperation1(string myValue)
{
return "Hello: " + myValue;
}
public string MyOperation2(DataContract1 dataContractValue)
{
return "Hello: " + dataContractValue.FirstName;
}
public string HelloWorld(string str)
{
StartEvent(str);
//return "Helloworld from " + str;
}
}
[DataContract]
public class DataContract1
{
string firstName;
string lastName;
[DataMember]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[DataMember]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
}
What i want to do is to pass the client message from HelloWorld function to my main form, so i try to create event inside class service1:
public delegate void StatusEventHandler(string srt);
public event StatusEventHandler StartEvent;
but it remained null even after registered from my main form (inside my button click event)
What is the simplest way to achieve that ?
When you created a service host, you gave a type, which means that with every call to your service, a new instance of that type will be created.
host = new ServiceHost(typeof(ServiceLibrary.service1));
You need to pass an instance of your service to the servicehost constructor, so every call will use that instance.
var yourServiceInstance = new ServiceLibrary.service1();
// attach event handlers here
host = new ServiceHost(yourServiceInstance);
When you do this, your service class needs to be configured for single instance mode:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class service1
{
// methods here
}

Windows Communication Foundation Null Reference Error

I am trying to use Windows Communication Foundation(WCF) for testing purposes in my application and I am getting a null object. I am new to using WCF, so any help would be appreciated.
From what I understand, each host has one service type(class). I have several interfaces and classes I want to test. My goal is to make one master class that has instances of the others, so I don't have to have several hosts running. Below is some of my code.
Host-
public class AutomationWCFHost
{
public ServiceHost host;
public AutomationWCFHost() {
Uri httpUrl = new Uri("http://localhost:8090/MyFun");
host = new ServiceHost(typeof(MyFun), httpUrl);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
}
public void StartWCF() {
this.host.Open();
}
public void StopWCF(){
this.host.Close();
Interfaces -
[ServiceContract]
public interface IMyFun {
[OperationContract]
string Echo(string msg);
IDataSources DataSources { [OperationContract]get; [OperationContract]set; }
}
[ServiceContract]
public interface IDataSources {
[OperationContract]
void Add(DataSource dataSource);
[OperationContract]
void Remove(DataSource dataSource);
}
Class - I am trying to make one base class so I don't have to have multiple hosts running.
public class MyFun : IMyFun {
public DataSources _dataSources;
public MyFun () {
_dataSources = new DataSources();
}
public string Echo(string msg) {
return msg;
}
public IDataSources DataSources { get { return _dataSources; } set {_dataSources = (DataSources)value;} }
}
public class DataSources : IDataSources{
....//Several methods and properties
}
Test -
public void MyTest() {
EndpointAddress address = new EndpointAddress("http://localhost:8090/MyFun");
BasicHttpBinding binding = new BasicHttpBinding();
ChannelFactory<IMyFun> factory = new ChannelFactory<IMyFun>(binding, address);
IMyFun channel = factory.CreateChannel();
Assert.AreEqual("Test", channel.Echo("Test"));
IDataSources dataSources = channel.DataSources;
dataSources.Add(newDataSource);
}
As I step through, it runs the echo method fine, but when it does
IDataSources dataSources = channel.DataSources;
dataSources is null. This makes it so
dataSources.Add(newDataSource);
fails due to null exception error.

my first WCF server - works with "string" but doesn't work with custom interface?

I've implemented my first WCF application.
I need one method IConsoleData GetData();
I receive CommunicationException "There was an error reading from the pipe: The channel was closed. (109, 0x6d)." in client.
When I replaced IConsoleData GetData(); to string GetData(); application become functionable.
How should I fix the code to use IConsoleData GetData()?
Server:
//public interface IConsoleData
//{
// double GetCurrentIndicator();
//}
//public class IConsoleDataImpl : IConsoleData
//{
// public double GetCurrentIndicator()
// {
// return 22;
// }
//}
[ServiceContract]
public interface IMBClientConsole
{
[OperationContract]
//IConsoleData GetData();
string GetData();
}
public class MBClientConsole : IMBClientConsole
{
//public IConsoleData GetData()
//{
// return new IConsoleDataImpl();
//}
public string GetData()
{
//return new IConsoleDataImpl();
return "hello";
}
}
class Log
{
private ServiceHost _host;
public void initialize()
{
_host = new ServiceHost(typeof (MBClientConsole),
new Uri[]
{
new Uri("net.pipe://localhost")
});
_host.AddServiceEndpoint(typeof(IMBClientConsole),
new NetNamedPipeBinding(),
"PipeReverse");
_host.Open();
System.Threading.Thread.Sleep(1000000);
// TODO: host.Close();
}
}
Client:
//public interface IConsoleData
//{
// double GetCurrentIndicator();
//}
[ServiceContract]
public interface IMBClientConsole
{
[OperationContract]
//IConsoleData GetData();
string GetData();
}
class Program
{
static void Main(string[] args)
{
ChannelFactory<IMBClientConsole> pipeFactory =
new ChannelFactory<IMBClientConsole>(
new NetNamedPipeBinding(),
new EndpointAddress(
"net.pipe://localhost/PipeReverse"));
IMBClientConsole pipeProxy =
pipeFactory.CreateChannel();
while (true)
{
string str = Console.ReadLine();
Console.WriteLine("pipe: " +
//pipeProxy.GetData().GetCurrentIndicator());
pipeProxy.GetData());
}
}
}
if you use an Interface you have to do two things:
the implementations needs to be serializable (use the DataContract - Attribute)
you have to tell the service the known types if you use the interface (here it's IConsoleDataImpl)
Make your live easier and decorate the implementation with DataContract and the members of it with DataMember Attributes and use the implementation instead of the interface.
You can find a lot about this here
(and don't begin the implementations name with "I")
here is a rework without the known-type stuff and I think this will help you for your
first steps - no need to dig in that deep (Interfaces + KnownTypes) yet.
[DataContract]
public class ConsoleData
{
[DataMember]
public double CurrentIndicator
{
get { return 22; }
set { /* whatever */ }
}
}
[ServiceContract]
public interface IMBClientConsole
{
[OperationContract]
ConsoleData GetData();
}
You have a couple problems there. First, your interface should be marked as a [DataContract]
[DataContract]
public interface IConsoleData
{
double GetCurrentIndicator();
}
Now, when WCF sends an IConsoleData over the wire, it is going to serialize the data in the class, send it, and deserialize it on the client. The problem with your implementation is that it contains nothing that can be serialized.
public class IConsoleDataImpl : IConsoleData
{
public double GetCurrentIndicator()
{
return 22;
}
}
If you were to build a client using svcutil.exe from the above, it would create the IConsoleDataImpl class, but the GetCurrentIndicator method wouldn't do anything. Important distinction here: WCF will transmit DATA, not IMPLEMENTATION.
What you probably want here is something more like:
[DataContract]
public interface IConsoleData
{
[DataMember]
double CurrentIndicator { get; set; }
}
public class ConsoleDataImpl : IConsoleData
{
public double CurrentIndicator { get; set; }
}
[ServiceContract]
[KnownType(typeof(ConsoleDataImpl))]
public interface IMBClientConsole
{
[OperationContract]
IConsoleData GetData();
}
public class MBClientConsole : IMBClientConsole
{
public IConsoleData GetData()
{
return new IConsoleDataImpl() { CurrentIndicator = 22 };
}
}
Although at this point the IConsoleData interface isn't really needed, and I would just remove it.
But basically the thing to remember is in general, you want your WCF services to contain methods, and your data contracts to contain properties (or fields). The implementation inside a method in a DataContract will not be in the client if you generate a client from he WSDL, that would only work if you were to copy over a shared dll with the DataContracts to the client.
Hope that makes sense...

Categories

Resources