I have created one WCF service Application. There are few methods in Service1.svc.
Here is my IService1.cs
[OperationContract]
GetUserDetailsByEmail_Result GetUserDetailsByEmail(string email);
Here is my Service.svc.cs
public class Service1 : IService1
{
#region GetUserDetails
public GetUserDetailsByEmail_Result GetUserDetailsByEmail(string email)
{
return (new UserManager()).GetUserDetailsByEmail(email);
}
#endregion
}
Here GetUserDetailsByEmail_Result is Complex type created in DemoModel.edmx. It contain some Scalar Property.
Basically what I am trying to do is, I want to call this method from Client(c#) side. Here is my Client Side code
//svc.GetUserDetailsByEmailCompleted += new EventHandler<GetUserDetailsByEmailCompletedEventArgs>(svc_GetUserDetailsByEmailCompleted);
GetUserDetailsByEmail_Result dtbUserDetails = svc.GetUserDetailsByEmailAsync(loginName);
Here svc is the object of Service1Client. Here I am simply calling wcf method. It gives me an error
Cannot implicitly convert type 'void' to 'Demo.DemoServiceReference_Client.GetUserDetailsByEmail_Result'
It works when I use svc_GetUserDetailsByEmailCompleted method. But I want the return data directly in dtbUserDetails. How can I achieve this? Is there any changes in my WCF service or in my client side? Or in WCF method declaration?
You either need to create an object and bind the data to it like some of the people in the comments suggested then mark each property like so:
[DataContract(Namespace = "MyServiceContract.Service1.ComplexObject")]
public class ComplexObject
{
[DataMember(Order = 1, IsRequired = true)]
public String DbItem1{ get; private set; }
[DataMember(Order = 2, IsRequired = false)]
public ComplexBlobData DbItem2{ get; set; }
}
Or if you can open up the DemoModel.edmx(Code Behind) and mark it all with data contract the same way you would mark your own object.
Bottom line anything not marked is not going over the wire.
Related
I have a WCF Restful service call which expects multiple parameters.
Consider the following data and service contracts.
public class ClassA
{
public string aString{ get; set;}
public int aInt {get; set;}
}
public class ClassB
{
public string bString{ get; set;}
public int bInt {get; set;}
}
[ServiceContract]
public interface ISampleService
{
[OperationContract(IsOneWay = false)]
ClassC GetSomeData(ClassA classA, string sValue, ClassB classB);
}
I have a C#/winform based test application.
I know all these parameters need to be wrapped before calling the service.
I'm having difficulty figuring out what the C# code to call the service would look like on the client side.
Can someone show me an example of how I would structure the code on the client side to call the above defined service?
Thanks,
JB
You should be able to call the service like a normal method after setting up an endpoint for ISampleService.
var result = ISampleService.GetSomeData(
new A { aString = "A string" },
"someValue",
new B()
);
WCF does its magic to transform this to a remote procedure call. Just make sure all the parameters you want to pass are serializable.
The easiest way I figured out to do this was to create a RESTFul interface that accepts an ArrayList.
On the client side, the desired complex parameters (classes) are serialized to a string and then inserted into the ArrayList.
On the service side :
1) Verify the ArrayList contains the desired number of parameters
2) Deserialize the complex objects from the incoming ArrayList
I'm not sure if this is the most elegant or 'accepted" way to do this, but it works.
I have a WCF Service that based on Writing Highly Maintainable WCF Services. Requests are processed using a CommandService:
[WcfDispatchBehaviour]
[ServiceContract(Namespace="http://somewhere.co.nz/NapaWcfService/2013/11")]
[ServiceKnownType("GetKnownTypes")]
public class CommandService
{
[OperationContract]
public object Execute(dynamic command)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(command);
return command;
}
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
var coreAssembly = typeof(ICommandHandler<>).Assembly;
var commandTypes =
from type in coreAssembly.GetExportedTypes()
where type.Name.EndsWith("Command")
select type;
return commandTypes.ToArray();
}
}
Everything works great (thanks Steve) but now I need to add the ability to upload a file to the service. From what I've read and based on errors received during testing, WCF needs to use a [MessageContract] when uploading a file using a Stream. So I've decorated my command class and put the non-Stream members into the message header, and updated my binding definition to use streaming:
[MessageContract]
public class AddScadaTileCommand
{
[MessageHeader(MustUnderstand = true)]
public int JobId { get; set; }
[MessageHeader(MustUnderstand = true)]
public string MimeType { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Name { get; set; }
[MessageBodyMember(Order = 1)]
public Stream Content { get; set; }
}
Unfortunately when I call the service with a file to upload I get an error:
There was an error while trying to serialize parameter
http://somewhere.co.nz/NapaWcfService/2013/11:command. The
InnerException message was 'Type 'System.IO.FileStream' with data
contract name
'FileStream:http://schemas.datacontract.org/2004/07/System.IO' is not
expected.
So I added a new method to the service specifically for the file upload request:
[OperationContract]
public void Upload(AddScadaTileCommand addScadaTileCommand)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(typeof(AddScadaTileCommand));
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(addScadaTileCommand);
}
This works perfectly, unless I change the AddScadaTileCommand parameter to dynamic in the method definition, in which case I get the same error as above. This appears to indicate that the [MessageContract] attributes are not applied or ignored when using dynamic as the type of the parameter. Is there any way to resolve this or will I need to create separate methods for requests that involve streams?
How do I return a class with List<T> objects from a wcf service to a client?
Can someone show me an example on how to return a class with some list objects and assign it to the client? I am getting this error on the client when I try to assign the class with Listobjects to local variables on the form
Error: Cannot implicitly convert type 'System.Collections.Generic.List<TesterWCFService.ServiceRef.TypeCode>' to 'System.Collections.Generic.List<Project1.TypeCode>'
Code:
public interface ICodesService
{
[OperationContract]
CodesList LoadCodeData();
}
[Serializable]
[DataContract]
public class CodesList
{
[DataMember]
public List<TypeCode> TypeCodes{ get; set; }
[DataMember]
public List<TypeCode1> TypeCodes1{ get; set; }
}
LoadCodes.svc
public class LoadCodesService : ICodesService
{
CodesList _AllCodes = new Codes();
public CodesList LoadCodeData() {
using (CodeEntities _codes = new CodeEntities()) {
_AllCodes.TypeCodes= _codes.TypeCode.ToList();
_AllCodes.TypeCodes1= _codes.TypeCodes.ToList();
}
return _AllCodes
}
}
On the Client:
public class Codes
{
public List<TypeCode> TypeCodes{ get; set; }
public List<TypeCode1> TypeCodes1{ get; set; }
}
This is the same class as CodesList on the ICodesService. I am declaring it at both placing ICodesService and the client. I want to be loading it on the webservice and assigning it on the client
private void button1_Click(object sender, EventArgs e)
{
public Codes _codesInProxy = new Codes();
LoadCodesServiceReference.CodesServiceClient proxy = new LoadCodesServiceReference.CodesServiceClient();
proxy.CodesList _codesList;
_codesList= proxy.LoadCodeData();//this one returns the codeslist from the service
_codesInProxy.TypeCodes = codesList.TypeCodes.ToList()
// This one gives an error
//Now I would like assign it to the class on the client and use it
}
Error: Cannot implicitly convert type 'System.Collections.Generic.List<TesterWCFService.ServiceRef.TypeCode>' to 'System.Collections.Generic.List<Project1.TypeCode>'
You can/should use the same object reference as that created in the WCF service.
The wcf service will expose the objects defined in the interface, and you should create you client side objects, as references of the WCF objects.
When adding the service reference, Visual Studio already creates all the empty class structures, you can just use them.
Pseudo Code:
New var as wcfserviceReferenceInstance.object
It seems that problem is that you have two different definitions for storing call resulton client side - one generated by adding WCF Service Reference (proxy.CodesList) and another defined manually (Codes).
You don't need to re-define server side classes once more in client side. If you add Service Reference then all the data types will be generated automatically. Just change client side so you use proxy.CodesList everywhere.
I'm writing a WCF webservice and passing in a complex type as a parameter of the method. The complex type looks like this:
[DataContract(Namespace = "")]
public class MyRequest
{
[DataMember()]
public string TransactionId { get; set; }
[DataMember(IsRequired = true)]
public bool IsRollback { get; set; }
[DataMember(IsRequired = true)]
public OrderType OrderType { get; set; }
[DataMember(IsRequired = true)]
public ICustomerId CustomerId { get; set; }
[DataMember()]
public long OrderId { get; set; }
[DataMember()]
public AnotherComplexType PurchaseInfo { get; set; }
The webservice method looks like this:
[ServiceKnownType(typeof(CustomerIdByName))]
[ServiceKnownType(typeof(CustomerIdByAccount))]
public OrderResult Execute(MyRequest order) {
}
The Interface looks like this:
[KnownType(typeof(CustomerIdByAccount))]
[KnownType(typeof(CustomerIdByName))]
public interface ICustomerId{
string GetId();
}
When I make a request using the SOAP end point, everything works just great. But when passing the request to the REST end point, I get the serialization error.
This is the request I'm using
<MyRequest>
<CustomerId>
<AccountId>59251</AccountId>
</CustomerId>
<IsRollback>false</IsRollback>
<OrderId>0</OrderId>
<OrderType>OrderSubscription</OrderType>
<PurchaseInfo>
<ObjectId>196521</ObjectId>
</PurchaseInfo>
<TransactionId>ABC123</TransactionGuid>
</MyRequest>
Since I had been stuck at this point for too long, I then changed the ICustomerId member to be an abstract class that implements ICustomerId. Again the SOAP end point works fine but sending the request to the rest end point I get an error that states "Cannot create abstract class"
What am I missing or doing wrong here?
Is this failing because the interface is nested in the complex type and not a direct parameter of the webservice method? I've used webservices that receive interfaces as parameter and with the KnownType decorators they work just fine. Same question applies to the abstract class, is this not working because the abstract class is nested within a member of the MyRequest complex type?
This is the error message I am getting:
Element CustomerId from namespace cannot have child contents to be deserialized as an object. Please use XmlNode[] to deserialize this pattern of XML
Have you tried decorating your interface as a RESTful method?
[ServiceContract]
public interface IMyRequest
{
[OperationContract]
[WebInvoke(
UriTemplate = "Requests/GetID",
Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped)]
string GetId(MyRequest myRequest);
...
Also:
make sure that the [DataMember] properties match your request payload. Everything that gets passed in your XML request payload must be caught by the serializer in the service. I recommend keeping the naming the same, but you can map it using [DataMember(name="MyProperty")]. Also, your [DataContract] must be mapped to match the name of the parent node of your XML payload like [DataContract(Name="MyRequest")] but only if the class is named differently than the xml node. Do this, and it will deserialize your xml into the server side object/dto
The error you're getting sounds like it's complaining about the complex type inside of your DataContract. Your complex type needs to be decorated for serialization the same as your MyRequest type.
Also ensure your REST endpoint is bound to webHttpBinding
It looks like serverside cannot receive the passed values, requestVersion.Ping is empty.
namespace Communication
{
public class DataForRequestVersion
{
public string Ping = "";
}
public class DataForResponseVersion
{
public string Pong = "";
public string Version = "";
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
Communication.DataForResponseVersion Version(Communication.DataForRequestVersion requestVersion);
}
//Server
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceImplementation : WCFSimple.Contract.IService
{
public Communication.DataForResponseVersion Version(Communication.DataForRequestVersion requestVersion)
{
//Here requestVersion.Ping is EMPTY
Communication.DataForResponseVersion responseVersion = new Communication.DataForResponseVersion();
responseVersion.Pong = requestVersion.Ping;
responseVersion.Version = "MyApp v" + Settings.version;
return responseVersion;
}
}
//Client makes a request here
Communication.DataForRequestVersion requestVersion = new Communication.DataForRequestVersion();
requestVersion.Ping = DateTime.Now.ToString(Settings.DayTimePreciseFormat);
//Here requestVersion.Ping has a value
Communication.DataForResponseVersion responseVersion =
Service.Version(requestVersion);
What am I missing?
UPDATE
My application works very well, both sides communicate by passing custom data classes without any problem. However I tried to modify test client which one only sends-receives current time as string and made its parameter a bit complex; from string to custom data class. Main solution's application can send Version request and receive the answer. So I think my little test client got a problem.
Here is the pastebin code:
2nd UPDATE:
Some moderator doesn't allow me to answer my own question, I don't know why, I found a very similar question and the guy answered his own too. To help others next time I'm explaining the reason; I used namespace instead of creating class...And I fixed:
//namespace Communication
public class Communication
You need to label your request (and response) classes with the [DataContract] attribute, and their properties with the [DataMember] attribute:
[DataContract]
public class DataForRequestVersion
{
[DataMember]
public string Ping = "";
}
try using [DataContract] on your Data...classes... and [DataMember] on their fields...
Change the fields in DataForRequestVersion DataForResponseVersion classes to properties. By Default DatacontractSerializer will serialize public properties.