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
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();
}
}
I am trying to test a WCF web service and am having problems when I introduce complicated return types. I have tried to keep this simple to start and am verifying I can pass data between the web service and a client. I have created the following files: MyService.svc, MyService.cs, and IMyService.cs. This is done in ASP.NET C#.
MyService.cs looks like this:
public class MyService : IMyService
{
public void DoWork()
{
}
public bool BoolTest()
{
return false;
}
public string StrTest(string input)
{
return input;
}
}
IMyService.cs Looks like this:
[ServiceContract]
public interface IMyService{
[OperationContract]
void DoWork();
[OperationContract]
bool BoolTest();
[OperationContract]
string StrTest(string input);
}
I have a console application which I connect to the Service above with a "Service Reference". When I use the code shown above, everything links up okay and I am able to make calls to the functions provided by the web service.
My problem occurs when I try to return anything more complicated such as an array, a list, or a custom object. After I add a function to the Web Service with one of these return types, then attempt to update the Service Reference in the Console Application, the reference fails to update and is lost.
Here is MyService.cs with the additional method that causes the failure:
public class MyService : IMyService
{
public void DoWork()
{
}
public bool BoolTest()
{
return false;
}
public string StrTest(string input)
{
return input;
}
public string [] StrArrTest()
{
string[] s = new[] { "test", "test2" };
return s;
}
}
Here is a copy of IMyService.cs with the additional method added that causes the error:
[ServiceContract]
public interface IMyService{
[OperationContract]
void DoWork();
[OperationContract]
bool BoolTest();
[OperationContract]
string StrTest(string input);
[OperationContract]
string [] StrArrTest();
}
If I use a List<string> as a return type or MyObject as a return type it also fails.
I am guessing there is some sort of serialization needed but am having a hard time tracking down exactly what I need to do.
Additional bits of information that might be helpful... The client and host are running on the same machine. When I access http://localhost/MyService.svc in a web browser there aren't any errors shown. When I access http://localhost/MyService.svc?wsdl a descent sized xml document appears.
Can anyone help explain what I need to do so I can return arrays and lists of objects in the web service?
I found this similar question, why does my silverlight reference to my wcf service blow up when I add a method to the wcf service that returns a generic list, but it applies to SilverLight which I am not using. I took a look at the Reference.cs file mentioned in the post, when everything is working it is populated with a bunch of stuff. When the reference is not updated correctly (after the method that returns an array is added), the Reference.cs file is empty with the exception of a comment about the file being auto-generated.
I have a WCF service code like this:
[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class SomeService
{
public string Password { [OperationContract] get; [OperationContract] set; }
public void CheckPassword()
{
if (Password == null || Password != "password")
throw new FaultException("Invalid Password");
}
[OperationContract]
public string SomeMethod()
{
this.CheckPassword();
return "Some Data";
}
}
And the client windows application consumes it like this:
public class ClientClass
{
public ClientClass()
{
STASomeService.Value.SomeMethod();
}
}
public class ClientClass
{
public ClientClass()
{
STASomeService.Value.set_Password("password");
}
}
How can I reset the value of SomeService.Password whenever the SomeService class is instantiated? I do not want an attacker to access my service methods, but when the actual client set the password, the passwords stays in the SomeService.Password property in every service call. But I want to retain the Password value per instance because the client needs that.
My code is in C#, framework 4, build in VS2010 Pro.
Please help. Thanks in advance.
You shouldn't have to reset the value of SomeService.Password because it isn't static. Are you seeing something to the contrary?
Since you're using InstanceContextMode.Single (which I originally overlooked), your best recourse my be to mock the behavior of having individual instances in your network bound singleton. The only way I can think of to facilitate this is to have a proxy service class that matches your service's contracts and delegates its calls to custom instances based on specific criteria (which would define the session). It would be cumbersome to maintain this way and adds a unnecessary level of abstraction, but (in my head at least) it should work
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.
I have Googled and read for hours now and I can't find anyone that deals with my specific scenario...
I want to use interfaces in my WCF service contracts to loosely couple the service from the classes used on each end of the wire. This will enable us to have a low-level assembly that contains just the Service and Data Contracts (just interfaces) that we can hand to a consultant. On their end of the wire they can instantiate their data classes that implement our Data Contract interface, send it over the wire to us, and our WCF service will then translate/cast/whatever that incoming data into our version of a data class that implements the same interface.
Here's an example. IDataContract contains the bare information I want to transmit over the wire. The endpoints and other WCF-specific config are all default stuff (my problems may lie in that, so I can include more of it if that's where I need to change things).
EDIT: I've included more of the code and renamed a couple classes to help it be less confusing. The Name & Namespace additions to the DataContractAttributes, as well as the two sections in the config files are new additions based on information from this blog post. If I switch to an abstract base class instead of an interface, it works. However, I'd like to get this working with an interface if possible.
Shared library (my code, shared with client authors):
public interface IDataContract
{
string MyProperty { get; set; }
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
IDataContract TestSharedInterface(IDataContract clientData);
}
Client code (their's):
[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ClientDataClass : IDataContract
{
[DataMember]
public string MyProperty { get; set; }
}
private static void CallTestSharedInterface()
{
EndpointAddress address = new EndpointAddress("http://localhost/ServiceContractsTest.WcfService/TestService.svc");
ChannelFactory<ITestService> factory = new ChannelFactory<ITestService>("ITestService", address);
ITestService proxy = factory.CreateChannel();
((IClientChannel)proxy).Open();
IDataContract clientData = new ClientDataClass() { MyProperty = "client data" };
IDataContract serverData = proxy.TestSharedInterface(clientData);
MessageBox.Show(serverData.MyProperty);
}
Client config:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
<knownType type="ServiceContractsTest.WcfClient.ClientDataClass, ServiceContractsTest.WcfClient"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
Server code (mine):
public class TestService : ITestService
{
public IDataContract TestSharedInterface(IDataContract clientData)
{
ServerDataClass convertedClientData = (ServerDataClass)clientData;
IDataContract serverData = new ServerDataClass() { MyProperty = convertedClientData.MyProperty + " + server data added" };
return serverData;
}
}
[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ServerDataClass : IDataContract
{
[DataMember]
public string MyProperty { get; set; }
}
Server config:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
<knownType type="ServiceContractsTest.WcfService.ServerDataClass, ServiceContractsTest.WcfService"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
I am getting a serialization error on the client call complaining about known types. Am I just missing some metadata markup in that client class? I'm at a loss as to where to even know the problem even lies, as I've tried all the searches I can think of and no one seems to have dealt with this specific scenario.
Basically, I want ClientDataClass to serialize to <IDataContract><MyProperty>client data</MyProperty></IDataContract> and then be able to deserialize that into a ServerDataClass instance. This seems like it should be possible.
If your data contracts are interfaces WCF can't know what object to instantiate for an incoming request. There is no need for the class to be the same as in the service, after all the Add Service Reference reads the WSDL and generates new classes based on the type info in the WSDL.
This blog gives me the right direction to find the solution for my problem. Actually I have exactly the same scenario like sliderhouserules describes in his post.
But in my scenario I can't use any abstract or base class to inherit from. So I used a TypesHelper class to read the dataContractSerializer section by myself and pass the relevant types to the WCF service.
namespace ExampleNamespace
{
public interface IJustAInstance { }
[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(ExampleNamespace.TypesHelper))]
public interface ICreateInstance
{
IJustAInstance CreateInstance();
}
public static class TypesHelper
{
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
DataContractSerializerSection section = (DataContractSerializerSection)
ConfigurationManager.GetSection(
"system.runtime.serialization/dataContractSerializer");
if (dataContractSerializerSection != null)
{
foreach (DeclaredTypeElement item in dataContractSerializerSection.DeclaredTypes)
{
foreach (TypeElement innterItem in item.KnownTypes)
{
Type type = Type.GetType(innterItem.Type);
if (typeof(IJustAInstance).IsAssignableFrom(type ))
yield return type;
}
}
}
}
}
}
You could create a BaseContract that your ClientContract and ServerContract can provide (as property) and that you can use in the respective constructor when creating new instances of the ClientContract or ServerContract.
Then you only have to add the BaseContract to your shared lib.