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);
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 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
/* SERVICE CONTRACT */
[ServiceContract]
public interface IEntity
{
[OperationContract]
CustomerDetail GetCustomer(string entityID);
//string GetCustomer(string entityID);
[OperationContract]
List<CustomerDetail> GetCustomerList(List<string> entityIDList);
}
/* DATA CONTRACT */
[DataContract]
public class CustomerDetail
{
[DataMember]
public Customer customerDetail; //Customer is defined in the webservice i'm calling
}
/* Actual service contract implementation */
public CustomerDetail GetCustomer(string ID)
{
ThirdParty tpws = new ThirdParty();
var c = tpws.GetCustomerByCustomerID(ID);
CustomerDetail cd = new CustomerDetail();
999 cd.customerDetail = c[0];
return cd;
}
When I run this in VS2010 with breakpoint at line labelled 999 above, everything works well but the return fails to show anything in WCF Test Client - returning and error - An error occurred while receiving the HTTP response to ...
This is just a guess, but stop in the debugger before line "999" and look at what's in c. I bet that c.Length == 0.
Have you verified that type 'Customer' is serializable (is it decorated with the DataContractAttribute, SerializableAttribute, or similar)?
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 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