WCF - Cannot create abstract class - c#

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

Related

WCF OperationContract and dynamic parameter

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?

Call WCF method from client c#

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.

Returning List<T> with WCF service

I got an Employee class and each employee has a list of applied leaves. Is it possible to have the list AppliedLeave as a [DataMember] in WCF?
[DataContract]
public class Employee
{
[DataMember]
public string UserID { get; set; }
[DataMember]
public int EmployeeNumber { get; set; }
[ForeignKey("EmployeeUserID")]
[DataMember]
public List<Leave> AppliedLeave
{
get { return _appliedLeaves; }
set { _appliedLeaves = value; }
}
private List<Leave> _appliedLeaves = new List<Leave>();
...
}
Is there any other way to do this?
thank you for your consideration of this matter
I extend my Question
This is my Leave Class:
[DataContract]
public class Leave
{
[Key()]
[DataMember]
public Guid LeaveId { get; set; }
[DataMember]
public string LeaveType { get; set; }
[DataMember]
public DateTime StartDate { get; set; }
[DataMember]
public string EmployeeUserID { get; set; }
}
this shows ServiceContract ---->
[ServiceContract]
public interface IEmployeeService
{
[OperationContract]
Employee GetEmployeeByUserId(string userId);
[OperationContract]
void AssignSupervisor(string userId, string supervisorUserId);
[OperationContract]
void DeleteEmployeeByUserId(string userId);
....
}
In Client application,
EmployeeServiceClient employeeService = new EmployeeServiceClient();
Employee employee = employeeService.GetEmployeeByUserId(id);
But when Employee gathered from the service its shows Null for leaves,
Can somebody help me? what have I done wrong here?
Yes, it is possible to return generics from WCF service operations.
But by default they are casted to Array on client side. This can be customized while proxy generation.
WCF: Serialization and Generics
Also you have to decorate the service with all the types to which generics can be resolved, using KnownTypeAttribute.
Known Types and the Generic Resolver
I also found my server side list would always arrive at the client as a null pointer. After browsing around a lot for this problem it strikes me it is nearly always denied at first ("your code should work")
Found the issue.. I had configured my solution using one "WCF Service" project and one "Winforms app" project with a generated service reference. Both interface and implementation of Service1 were in the WCF service project, as expected. But any list member returned null.
When I put my IService1.cs = the interface only = in a separate class library instead, reference the class library on both sides (using) and generate the service reference again, my list does work ! The generated code on the client side looks much simpler.
I did not need any special attributes, change service reference configuration, or interface references for this.
You could use IList<T> instead of List<T>.

Using a generic object to return data from a webservice method?

I have an ASMX webservice with a number of methods which will return XML.
The service returns various different objects and I have created a wrapper object which contains information about the request e.g:
[Serializable]
[XmlRoot("response")]
public class DtoWrapper<T>
{
[XmlElement("error")]
public bool Error { get; set; }
[XmlElement("error_message")]
public string ErrorMessage { get; set; }
[XmlElement("success")]
public bool Success { get; set; }
[XmlElement("friendly_message")]
public string FriendlyMessage { get; set; }
[XmlArray("result")]
[XmlArrayItem("item")]
public List<T> Payload { get; set; }
}
Now this works fine until I defined my second method with a different type. Then I get this error when I try and load the ASMX test page
The top XML element 'response' from namespace 'http://tempuri.org/'
references distinct types
MyProject.Web.webservices.DtoWrapper1[MyProject.BusinessLogic.ClassA]
and
MyProject.Web.webservices.DtoWrapper1[MyProject.BusinessLogic.ClassB].
Use XML attributes to specify another
XML name or namespace for the element
or types.
I have tried marking my objects up with [XmlType(Namespace="com.temp.A")] and [XmlType(Namespace="com.temp.B")] but it doesn't seem to help.
Any ideas? Will I have to create a wrapper object for each type I want to use?
EDIT: I've realised it's not actually the type arguments that are the problem. It's the fact that the [XmlRoot] tag is specified on the class. The serializer is treating them as 2 types but they have the same root element in the same namespace.
You cannot do this. XML has no concept of generics, neither do XML Schema or SOAP. As far as XML Schema is concerned, if it has the same element name and same namespace, then it's the same thing.
You cannot have a generic web service, as the concepts do not exist.

Can't deserialize XML in WCF REST service

I've just started playing with the REST starter kit, and I've hit a road block trying to build my own service. I'm trying to create a service for account management, and I can't get the service to serialize my objects, throwing the following error:
Unable to deserialize XML body with root name 'CreateAccount' and root namespace '' (for operation 'CreateAccount' and contract ('Service', 'http://tempuri.org/')) using DataContractSerializer. Ensure that the type corresponding to the XML is added to the known types collection of the service.
Here's the actual code for the service (based off of the 'DoWork' method that came with the project):
[WebHelp(Comment = "Creates a Membership account")]
[WebInvoke(UriTemplate = "CreateAccount", RequestFormat = WebMessageFormat.Xml)]
[OperationContract]
public ServiceResponse CreateAccount(CreateAccount request)
{
try
{
// do stuff
return new ServiceResponse()
{
Status = "SUCCESS",
ErrorMessage = ""
};
}
catch (Exception ex)
{
return new ServiceResponse()
{
Status = "ERROR",
ErrorMessage = ex.Message + "\n\n" + ex.StackTrace
};
}
}
And last, but not least, here's the object that's causing all the trouble:
public class CreateAccount
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public bool SignUpForNewsletter { get; set; }
public string Password { get; set; }
}
Am I missing anything stupid?
Thanks in advance!
It turns out I was missing an extra value in the [DataContract] attribute on the business object.
Should be [DataContract(Namespace = "")]
It appears the problem is a namespace clash between your method name "CreateAccount" and your input type "CreateAccount".
Also, you have to mark your CreateAccount type as a DataContract like so:
[DataContract]
public CreateAccount
{
[DataMember]
public string LastName { get; set; }
...
}
If you want to keep the same name, you can specify a namespace for the CreateAccount class.
I noticed you have a return type as well. Ensure the return type is marked with the DataContract attribute as well. Also, specify the return format like so:
ResponseFormat = WebMessageFormat.Xml
If you don't have it already, I think a [DataContract] attribute above your CreatAccount class.
I had a similar problem, but I did have the DataContract attribute. What I was missing though was the xmlns="http://uri.org" attribute from the root element when trying to read the xml back into the object.
e.g.
<Root_Element xmlns="http://uri.org"><Child_Element/>...</Root_Element>

Categories

Resources