While answering another question I bumped into this interesting situation Where WCF is happy to cast an interface with different number of members and from Different namespaces where normal .net runtime can't.
Can any one explain how WCF is able to do it and how to configure/force WCF to behave same as normal .net runtime. Please note that I know I should have only one interface and blah.. blah..
here is working code
using System;
using System.Runtime.Serialization;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
namespace MyClient
{
[ServiceContract]
public interface IService
{
[OperationContract]
string Method(string dd);
[OperationContract]
string Method2(string dd);
}
}
namespace MyServer
{
[ServiceContract]
public interface IService
{
[OperationContract]
string Method(string dd);
}
}
namespace MySpace
{
public class Service : MyServer.IService
{
public string Method(string dd)
{
dd = dd + " String from Server.";
return dd;
}
}
class Program
{
static void Main(string[] args)
{
string Url = "http://localhost:8000/";
Binding binding = new BasicHttpBinding();
ServiceHost host = new ServiceHost(typeof(Service));
host.AddServiceEndpoint(typeof(MyServer.IService), binding, Url);
host.AddDefaultEndpoints();
host.Open();
// Following line gives error as it should do.
//MyClient.IService iservice = (MyClient.IService)new MySpace.Service();
// but WCF is happy to do it ;)
ChannelFactory<MyClient.IService> fac = new ChannelFactory<MyClient.IService>(binding);
fac.Open();
MyClient.IService proxy = fac.CreateChannel(new EndpointAddress(Url));
string d = proxy.Method("String from client.");
fac.Close();
host.Close();
Console.WriteLine("Result after calling \n " + d);
Console.ReadLine();
}
}
}
There is no inconsistency.
// Following line gives error, as it should do, because the .NET types
// MyClient.IService and MySpace.Service are not related.
MyClient.IService iservice = (MyClient.IService)new MySpace.Service(); // ERROR !!
// Likewise, a WCF client proxy defined using MyService.IService as the contract
// cannot be cast to the unrelated .NET type MyClient.IService
ChannelFactory<MyService.IService> fac1 = new ChannelFactory<MyService.IService>(binding);
fac1.Open();
MyClient.IService proxy = (MyClient.IService)fac1.CreateChannel(new EndpointAddress(Url)); // ERROR !!
// but the service can be consumed by any WCF client proxy for which the contract
// matches the defined service contract (i.e. they both expect the same XML infoset
// in the request and response messages). There is no dependency between the .NET type
// used in the client code and the .NET type used to implement the service.
ChannelFactory<MyClient.IService> fac = new ChannelFactory<MyClient.IService>(binding);
fac.Open();
// Next line does not error because the ChannelFactory instance is explicitly
// specialised to return a MyClient.IService so the .NET type is the same... there is no cast
MyClient.IService proxy = fac.CreateChannel(new EndpointAddress(Url));
// NOTE: Thus far we have not done anything with the service in this case.
// If we call Method() it should succeed, since the contract matches. If we call
// Method2() the channel will fault as there is no matching operation contract in the service.
The .NET type system is a completely different concept to the WCF notion of service/operation/message/data contract. Just as well, otherwise you could never write a WCF client for a WCF service you didn't write yourself.
However, as the middle example shows, if you reuse the .NET type for the service contract in both service and client code, your expectation will be met.
Your MyClient.IService has the same method as MyServer.IService does WCF's channel factory thinks that the contract matches on the exposed url and hence processes the request.
Try changing your MyClient.IService method name and you can see it fail. Namespace are logical seperations as we know.
When you create a WCF Service and expose the wsdl it doesn't have any of your namespaces, unless you specify one in your configuration using bindingNamespace attribute in your endpoint element. Just try a sample and generate a proxy from the wsdl to see that the proxy doesn't have any namespace.
As long as the IService in your MyClient and MyServer namespace match your WCF code above would work
In regards to your code below:
MyClient.IService iservice = (MyClient.IService)new MySpace.Service();
You are trying to cast MySpace.Service explicitly to MyClient.IService where your "Service" doesnt implement your MyClient.IService and is correct according to OOP. Since you have all the code in a single file and is self hosted might be giving you the confusion.
Related
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();
}
}
As a matter of fact I already found a solution to my problem, But I am just curious.
I came across the following error message
" The method UnlockObject is not supported on this proxy. It can happen if the method is not marked with OperationContractAttribute or if the interface type is not marked with ServiceContractAttribute"
here is my interface :
[ServiceContract(CallbackContract = typeof(IServeurCallback), SessionMode.Required)]
public interface IServeur
{
[OperationContract(IsOneWay = true)]
void UnlockObject<T>(Guid ClientId, ObjectId toUnlock, string collectionName);
[...]
}
How it is implemented in my server
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Serveur : Proxy.IServeur
{
public void UnlockObject<T>(Guid ClientId, ObjectId toUnlock, string collectionName)
{
/*stuff using <T>*/
}
}
and how it is called from my client
if (this.comboBox1.SelectedItem.ToString() == "Projet")
this.channel.UnlockObject<ProjectClass.Project>(client._Guid, toSend, "collection_Project");
else if (this.comboBox1.SelectedItem.ToString() == "Object")
this.channel.UnlockObject<Object.c_Object>(client._Guid, toSend, "collection_Object");
else if (this.comboBox1.SelectedItem.ToString() == "ObjString")
this.channel.UnlockObject<ObjString.ObjString>(client._Guid, toSend, "collection_ObjString");
(this.channel was created this way
DuplexChannelFactory<Proxy.IServeur> factory;
/* do stuff to make it usable */
Proxy.IServeur channel = factory.CreateChannel();
I solved this by removing the
<T>
from all the function. Now my code is a little bit dirtier, but it is working fine
Why does this error message show up ?
You have exception because wsdl does not support open generic types and so WCF service cannot expose them on operation contracts as it uses wsdl to expose metadata of your operations.
It is possible to expose bounded generics in your code (see details) or since the <T> argument only identifies the type you can add it as another argument
[OperationContract(IsOneWay = true)]
void UnlockObject(Type objectType,Guid ClientId, ObjectId toUnlock, string collectionName);
When I run my WPF that uses my WCF Service Library through visual studio I get a WCF Service Host startup at the same time with my service starting my WCF Service Library, however when I click on the exe for my WPF in the debug folder it doesn't startup is there anyway to make it start in code as the following code I have believed would work doesn't.
try
{
host = new ServiceHost(typeof(Service1), new Uri("http://" + System.Net.Dns.GetHostName() + ":8733/DatabaseTransferWcfServiceLibaryMethod/Service1/"));
host.Open();
}catch(AddressAlreadyInUseException)
{
}
I'm trying not to use service references.
I'm no expert at this, but perhaps you're missing the binding. Here is the simplest example I can create of hosting and consuming a WCF service in code (you'll need to add references to System, System.Runtime.Serializaton, and System.ServiceModel, but otherwise, this code is complete).
using System;
using System.Runtime.Serialization;
using System.ServiceModel;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create the host on a single class
using
( ServiceHost host
= new ServiceHost
( typeof(MyService)
, new Uri("http://localhost:1234/MyService/MyService")
)
){
// That single class could include multiple interfaces to
// different services, each must be added here
host.AddServiceEndpoint
( typeof(IMyService)
, new WSHttpBinding(SecurityMode.None)
// Each service can have it's own URL, but if blank use the
// default above
, ""
);
// Open the host so it can be consumed
host.Open();
// Consume the service (this cuold be in another executable)
using
( ChannelFactory<IMyService> channel
= new ChannelFactory<IMyService>
( new WSHttpBinding(SecurityMode.None)
, "http://localhost:1234/MyService/MyService"
)
){ IMyService myService = channel.CreateChannel();
Console.WriteLine(myService.GetValue());
}
// Clean up
host.Close();
}
}
}
[ServiceContract]
public interface IMyService
{ [OperationContract] int GetValue();
}
public class MyService : IMyService
{ public int GetValue()
{ return 5;
}
}
}
Okay the error was way away from where I thought it was the error was it was running on my user interface thread so needed to add.
[ServiceBehavior( UseSynchronizationContext = false)]
If anyone else has this problem i hope this helps.
I know that I can point to some SOAP web service by adding web reference using visual studio.
But I need to do it from code.
How can I manually create web reference object in code and access all methods from that object?
Basically I want to avoid generating proxy classes.
If you can get a copy of the service contract (interface)(svcUtil can help with this) then you can include it in your project and use the ChannelFactory class to dynamically create a channel for the client to communicate with the service.
I tend to encapsulate it all up in a SAL (Service Application Layer) to re-use as required.
This is a simple (and in no way complete!) example demonstrating how to connect to a fictious time service and call the GetTime() operation without using a VS generated proxy:
public class TimeSAL : IDisposable
{
private ChannelFactory<ITimeService> timeServiceProxyFactory;
private ITimeService timeServiceProxy;
private ITimeService TimeService
{
get
{
//create channel factory if not there
if (timeServiceProxyFactory == null)
timeServiceProxyFactory = new ChannelFactory<ITimeService>(new BasicHttpBinding(), new EndpointAddress("http://url_to_my_timeservice_endpoint")); //
if (timeServiceProxy == null)
timeServiceProxy = amlProxyFactory.CreateChannel();
return timeServiceProxy;
}
}
public string GetTime()
{
return TimeService.GetTime();
}
public void Dispose()
{
//dispose of ChannelFactory and proxy.
//ensure you check for comm faults to abort before closing
}
}
Now I can use this SAL throughout my code as necessary:
....
using(TimeSAL timeSAL = new TimeSAL())
{
myBusinessObject.CurrentTime = timeSAL.GetTime();
}
....
If you are unable to get your hands on a copy of the service contract, a long-winded way is to handcraft the soap request. Fiddler or soapUI can help with what the message should look like.
Hope some of this helps.
I am using VSTS 2008 + .Net + C# 3.5 to develop WCF service (self-hosted as a Windows Service). From client side, I am using ChannelFactory to connect to WCF service. My confusion is, when I change client side code from "public string response" to "public string responseAliasName", the value of responseAliasName is null. But when change back to variable name response, the response value is correct (value is "hi WCF").
My confusion is, I think variable name should not matter as long as the layout is the same from client and server side. Any ideas what is wrong?
Server side code:
namespace Foo
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in Web.config.
[ServiceContract]
public interface IFoo
{
[OperationContract]
FooResponse Submit(string request);
}
[DataContract]
public class FooResponse
{
[DataMember]
public string response;
}
}
namespace Foo
{
// NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in Web.config and in the associated .svc file.
public class FooImpl : IFoo
{
public FooResponse Submit(string request)
{
FooResponse foo = new FooResponse();
foo.response = "hi WCF";
return foo;
}
}
}
Client side code:
namespace Foo
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in Web.config.
[ServiceContract]
public interface IFoo
{
[OperationContract]
FooResponse Submit(string request);
}
[DataContract]
public class FooResponse
{
[DataMember]
// public string responseAliasName;
public string response;
}
}
namespace FooTestClient1
{
class Program
{
static void Main(string[] args)
{
ChannelFactory<IFoo> factory = new ChannelFactory<IFoo>(
"IFoo");
IFoo f = factory.CreateChannel();
FooResponse response = f.Submit("Hi!");
return;
}
}
}
you can use
[DataContract(Name="ResponseAliasName")]
public string response;
on Server side and it will work as you expect, DataContract by default uses field or property name to serialize data, and the server can't find correct data
No, the member name is included in the serialization. If you change it on the client side, it can't deserialize back into that member.
George, why not just use "Add Service Reference" to create your client? As you can see, it's dangerous to create the client-side service contracts by hand.
My confusion is, when
I change client side code from "public
string response" to "public string
responseAliasName", the value of
responseAliasName is null. But when
change back to variable name response,
the response value is correct (value
is "hi WCF").
My confusion is, I think variable name
should not matter as long as the
layout is the same from client and
server side. Any ideas what is wrong?
The variable name that you use locally in your client is totally irrelevant - the server knows nothing about that. Given your code snippet from the client:
ChannelFactory<IFoo> factory = new ChannelFactory<IFoo>("IFoo");
IFoo f = factory.CreateChannel();
FooResponse response = f.Submit("Hi!");
This will work - no problem:
FooResponse myResponseVar1 = f.Submit("Hi!");
and so will this:
FooResponse someReallyCleverVariableNameWhateverItMightBe = f.Submit("Hi!");
But the DataContract of course is a shared element that the service and client have to agree on! You cannot locally change the names of the elements in the data contract - after all, that's what really describes how your calls are going to be turned into an XML message, and these bits and pieces have to stay in sync between the server and the client in order for the client to be able to turn the message received from the server back into an object for you to use.
The ServiceContract and the DataContract must be the same on both ends - that's the basic requirement, otherwise, pretty much nothing goes at all.
Marc