I have a WCF service that passes back and forth the following DataContracts:
[DataContract]
public class RequestWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class ResponseWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class FooDataContract
{
public FooDataContract(string data, Guid id)
{
Data = data;
ID = id;
}
[DataMember]
public string Data { get; set; }
[DataMember]
public Guid ID { get; set; }
}
It's called via a proxy class like this:
void CallService(string data)
{
var id = Guid.NewGuid();
var response = proxy.CallService(new RequestWrapper
{
new FooDataContract(data, id);
});
}
This is then passed (over the service) to the database via a repository using EF:
public void RepoMethod(FooDataContract foo)
{
var guid = foo.ID; // - Breakpoint here shows all zeros!
efContext.DoSomething(foo.Data, foo.ID);
}
Here's the service call:
public ResponseWrapper CallService(RequestWrapper request)
{
var foo = request.FooDataContract;
repository.RepoMethod(foo);
var response = new ResponseWrapper{ FooDataContract = foo };
return response;
}
Here's the proxy:
public class Proxy : IMyService
{
static readonly ChannelFactory<IMyService> channelFactory =
new ChannelFactory<IMyService>("IMyService");
ResponseWrapper CallService(RequestWrapper request)
{
return channelFactory.UseService(s => s.CallService(request));
}
}
internal static class UseServiceFunction
{
internal static R UseService<T, R>
(this ChannelFactory<T> channelFactory, Func<T, R> useService)
{
var service = channelFactory.CreateChannel();
try
{
R response = useService(service);
return response;
}
finally
{
var channel = service as ICommunicationObject;
try
{
if (channel.State != CommunicationState.Faulted) channel.Close();
}
catch { channel.Abort(); }
}
}
}
I've put a watch on the Guid in the VS debugger. When the service is called from a client web application, the generated Guid is a valid Guid of seemingly random hex characters. Great, that's working.
But when the data is serialized, goes over the wire, and comes out the other side (in my repository), the Guid is all zeros!
I've double, triple checked that the Guid is indeed marked with the [DataMember] attribute. I'm wondering if the extra layer of DataContract (how a FooDataContract is wrapped with the RequestWrapper data contract) is causing a serialization issue?
I think your problem here is that the constructor you've made in your DataContract class doesn't get passed to the proxy on the client side. WSDL won't know anything about this. Think of your data contracts as just a place to stick data with no other functionality. To confirm, you can look in the reference.cs class that got generated in the client when you added the service reference.
I'd suggest re-writing the code so that you explicitly set each of the values in your data contract rather than relying on the constructor.
You can also write a hand coded proxy that has whatever behavior you want and then share that file with the client. That would work, but then you'll be more tightly coupling your client to your service.
Turns out, my translation layer wasn't updated to convert between the DTOs! Whooooops!
Related
The system i am developing is using DataContractJsonSerializer.
The service looks like this:
[HttpPost, Route("RunQuery")]
public List<BIResultRecord> RunQuery(BIQuery query) {
// Logic
}
The BIQuery class hierarchy is as follows:
[DataContract]
[KnownType(typeof(BIQuery1))]
public class BIQuery
{
[DataMember]
public string Member { get; set; }
[DataMember]
public QueryTypeEnum QueryType { get; set; }
}
[DataContract]
public class BIQuery1 : BIQuery
{
public BIQuery1()
{
QueryType = QueryTypeEnum.Type1;
}
[DataMember]
public ClassSpecificObject Object { get; set; }
}
THE PROBLEM:
Although i am sending BIQuery1 as a json object it is always deserialized (in my RunQuery method) as the parent object (BIQuery).
WHAT I'VE TRIED:
I've removed the default formatter and added a new one:
public class DataContractJsonFormatter : JsonMediaTypeFormatter
{
public override DataContractJsonSerializer CreateDataContractSerializer(Type type)
{
return new DataContractJsonSerializer(type, new DataContractJsonSerializerSettings() { EmitTypeInformation = EmitTypeInformation.AsNeeded });
}
}
I serialized the object in .NET to see the JSON structure and
cloning it to the request. I saw that it adds
"__type" : "BIQuery1"
to the JSON, which i'm using as well and it changed nothing.
Please help!
MORE DETAILS
The .NET system (client and server) runs in production as is, so only minor changes currently allowed. I'm making also a web client that would work with the existing services over REST.
[HttpPost, Route("RunQuery")]
public List<BIResultRecord> RunQuery(BIQuery query) {
// Logic
}
Your argument in the RunQuery method is the of type BIQuery object. It is deserializing as a BIQuery object because that is the type you are telling the method it will be passed. If you are passing an object of the type BIQuery1 to the method, do this instead:
[HttpPost, Route("RunQuery")]
public List<BIResultRecord> RunQuery(BIQuery1 query) {
// Logic
}
I'm new to working with WCF and i'm struggling to understand whats happening.
I'm trying to retrieve data from my service, which in turn grabs it from the database. To simplify things I wanted to return an object containing the data so that it could just be used immediatly within the UWP app. However, I'm running into the following error when I try to consume the service:
Cannot implicitly convert type 'MBCMobile.ServiceReference1.TaskBreakdown_GetDataResponse' to 'MBCMobile.ServiceReference1.JobTestObject'
The following code is what I'm using:
[ServiceContract]
public interface IService1
{
[OperationContract]
JobTestObject TaskBreakdown_GetData(int task, int appliance, int job);
}
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class Service1 : IService1
{
public JobTestObject TaskBreakdown_GetData(int task, int appliance, int job)
{
JobTestObject jobData = data.TaskBreakdown_GetData(task, appliance, job); //object returned from sql query
return jobData;
}
}
To consume my service im using the following:
private async void GetString()
{
ServiceReference1.Service1Client service = new ServiceReference1.Service1Client();
JobTestObject datalist = await service.TaskBreakdown_GetDataAsync(6153, 18876, 18111);
}
JobTestObject has the same format in both the service and the UWP app. The service version literally exists as a collection of get/set methods and nothing else. The app version has additional methods, but the exact same get/set methods. I've tried using ServiceReference1.JobTestObject instead to see if that helps but to no avail. I expect that it might be a little naive of me to think an object can be passed to the client this way.
I also tried just returning a list of strings and got a similar error.
I've managed fine in the past with retrieving single values from the service, but now that I want to get multiple values at once I can't understand what I'm doing wrong. Sadly most of the solutions I've found don't relate to UWP apps
Update: As requested
[DataContract]
public class JobTestObject
{
int appliance_id, manu_id, model_id, landlord_app, applianceType_id,
[DataMember]
public int ApplianceType { get { return applianceType_id; } set { applianceType_id = value; } }
[DataMember]
public int Appliance_ID { get { return appliance_id; } set { appliance_id = value; } }
[DataMember]
public int ApplianceManufacturer { get { return manu_id; } set { manu_id = value; } }
[DataMember]
public int ApplianceModel { get { return model_id; } set { model_id = value; } }
}
I've a WCF service with one method which will be called from multiple web API controllers like in the below code.
public string Print(PdfPrinterRequest _request)
{
PdfPrinterService.PdfPrinterClient _Client = new PdfPrinterService.PdfPrinterClient();
PdfPrinterResponse _response = new PdfPrinterResponse();
return _Client.Print(_request.Document, out _pdfResponse);
}
PdfPrinterRequest(Document class) is the entity which I'm passing to get the response message from WCF service.Currently the document class holds few properties(REquest Header). I would like to call the same Print method from other API and pass type 'Customer' to WCF service. How can i achieve this? Can anyone please suggest me the correct implementation?
Below is my WCF service code,
public class PdfPrinterService : IPdfPrinter
{
public PdfPrinterResponse Print(PdfPrinterRequest request)
{
return PdfPrinterFacade.PrintPdf(request);
}
}
public static PdfPrinterResponse PrintPdf(PdfPrinterRequest request)
{
PdfPrinterResponse response = new PdfPrinterResponse();
//Process the request and send back the response message
}
[MessageContract]
public class PdfPrinterRequest
{
private Document _document;
[MessageBodyMember]
public Document Document
{
get { return _document; }
set { _document = value; }
}
}
How to pass a dynamic class object as a parameter in place of PdfPrinterRequest which is no bound to only one type(Document)? Please suggest.
Thanks,
If this service does not need to be interoperable, you can switch to NetDataContractSerializer, which uses full .NET type information, and is able to serialize many more types (but not any type - that's impossible).
Grab the UseNetDataContractSerializerAttribute from this answer and apply like so:
[UseNetDataContractSerializer]
public class PdfPrinterService : IPdfPrinter
{
public PdfPrinterResponse Print(PdfPrinterRequest request)
{
return PdfPrinterFacade.PrintPdf(request);
}
}
[MessageContract]
public class PdfPrinterRequest
{
[MessageBodyMember]
public object Document { get; set; }
}
I have couple of questions related to REST service implementation using ServiceStack.
For GET operation, I define my request DTO as below :
[Route("/Customer/{ID}", Verbs = "GET")]
public class GetCustomer : IReturn<GetCustomerResponse>
{
....
....
}
Here "GetCustomer" is request DTO and "GetCustomerResponse" is response DTO. But for PUT/POST/DELETE operation, I just need to know whether operation got committed successfully or not and if 'not' then what is the exception. So what should be my request dto definition for POST/PUT/DELETE? Should it use IReturnVoid as shown below?
[Route("/Customer/{ID}", Verbs = "DELETE")]
public class DeleteCustomer : IReturnVoid
{
....
....
}
If I have to use IReturnVoid then how I can retrieve any exception information that might occur on committing my operation?
In the error handling document for service stack it is written and I quote below
Error Response Types
The Error Response that gets returned when an Exception is thrown
varies on whether a conventionally-named {RequestDto}Response DTO
exists or not.
If it exists:
The {RequestDto}Response is returned, regardless of the service
method's response type. If the {RequestDto}Response DTO has a
ResponseStatus property, it is populated otherwise no ResponseStatus
will be returned. (If you have decorated the {ResponseDto}Response
class and properties with [DataContract]/[DataMember] attributes, then
ResponseStatus also needs to be decorated, to get populated).
Otherwise, if it doesn't:
A generic ErrorResponse gets returned with a populated ResponseStatus
property.
The Service Clients transparently handles the different Error Response
types, and for schema-less formats like JSON/JSV/etc there's no actual
visible difference between returning a ResponseStatus in a custom or
generic ErrorResponse - as they both output the same response on the
wire.
What I'm not getting from above is what should be the return type for my Delete method in my service implementation? How I can implement my delete method without defining delete response DTO but yet I'm able to retrieve 'ErrorResponse' n exception info?
Is it possible to define route with "DELETE" verb? I have following implementation.
Route:
[Route("/DeleteCustomer/{ID}", Verbs = "DELETE")]
public class DeleteCustomer : IReturn<DeleteCustomerResponse>
{
public int ID { get; set; }
}
Method implementation:
public DeleteContactResponse Delete(DeleteContact request)
{
.....
}
But whenever I call this delete using my client, I always get "NotFound" exception. I tried different clients but with all I get 404 error.
One of the reference link available alongwith Servicestack document reuses the "GET" and "DELETE" verb together.
Another link suggests not all browsers support delete operation.
So I wonder how Delete operation should be implemented?
See this earlier answer for details on how to design a REST-ful API with ServiceStack.
The CustomerRestExample contains a complete stand-alone example of a Customer REST ServiceStack Service:
Customer Service Definition
Here's an example of the custom Routes and Request DTO's of what a typical Customer REST Service could look like:
[Route("/customers", "GET")]
public class GetCustomers : IReturn<GetCustomersResponse> {}
public class GetCustomersResponse
{
public List<Customer> Results { get; set; }
}
[Route("/customers/{Id}", "GET")]
public class GetCustomer : IReturn<Customer>
{
public int Id { get; set; }
}
[Route("/customers", "POST")]
public class CreateCustomer : IReturn<Customer>
{
public string Name { get; set; }
}
[Route("/customers/{Id}", "PUT")]
public class UpdateCustomer : IReturn<Customer>
{
public int Id { get; set; }
public string Name { get; set; }
}
[Route("/customers/{Id}", "DELETE")]
public class DeleteCustomer : IReturnVoid
{
public int Id { get; set; }
}
OrmLite POCO Model:
public class Customer
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
Essentially the Custom Routes identify the Resource whilst the HTTP VERB indicates the operation on that Resource. Looking at the HTTP Requests makes this a little clearer:
GET /customers -> return all Customers
POST /customers -> Create a new Customer
GET /customers/1 -> return Customer 1
PUT /customers/1 -> Update Customer 1
DELETE /customers/1 -> Delete Customer 1
Customer Service Implementation
With the above DTO's definitions in-place, we can now implement this Customer REST Service by adding an implementation for each Request DTO - in this example using OrmLite:
public class CustomerService : Service
{
public object Get(GetCustomers request)
{
return new GetCustomersResponse { Results = Db.Select<Customer>() };
}
public object Get(GetCustomer request)
{
return Db.SingleById<Customer>(request.Id);
}
public object Post(CreateCustomer request)
{
var customer = new Customer { Name = request.Name };
Db.Save(customer);
return customer;
}
public object Put(UpdateCustomer request)
{
var customer = Db.SingleById<Customer>(request.Id);
if (customer == null)
throw HttpError.NotFound("Customer '{0}' does not exist".Fmt(request.Id));
customer.Name = request.Name;
Db.Update(customer);
return customer;
}
public void Delete(DeleteCustomer request)
{
Db.DeleteById<Customer>(request.Id);
}
}
Customer Usage Example
With the above Customer REST Service implementation, we can re-use the Request DTO's with ServiceStack's .NET Service Clients to provide an end-to-end Typed API without code-gen, i.e:
var client = new JsonServiceClient(BaseUri);
//GET /customers
var all = client.Get(new GetCustomers()); // Count = 0
//POST /customers
var customer = client.Post(new CreateCustomer { Name = "Foo" });
//GET /customer/1
customer = client.Get(new GetCustomer { Id = customer.Id }); // Name = Foo
//GET /customers
all = client.Get(new GetCustomers()); // Count = 1
//PUT /customers/1
customer = client.Put(
new UpdateCustomer { Id = customer.Id, Name = "Bar" }); // Name = Bar
//DELETE /customers/1
client.Delete(new DeleteCustomer { Id = customer.Id });
//GET /customers
all = client.Get(new GetCustomers()); // Count = 0
The comments above include the HTTP Operations performed in each Service Client example.
I got the fix for my 2nd question from following two links :
1. Link1
2. Link2
I don't fully understand this fix but doing above changes worked for me and now I can call Delete function from any clients.
For 1st question, pls refer #mythz 's reply below in detail.
What I would like to do is the following:
var client = new JsonServiceClient(ServiceUrl);
var request = new FooQuery {Id = 1};
IEnumerable<Project> response = client.Get(request);
However, my FooQuery doesn't implement any IReturn, and I'd like it not to (it's in a library without ServiceStack references). Here's my service side:
Library of business objects:
public class ProjectQuery
{
public int Id { get; set; }
}
AppHost:
Routes.Add<ProjectQuery>("/project", "GET");
Service:
public object Get(Foo request)
{
// do stuff.
}
Is there some nice, clean way to create the JsonServiceClient without using the IReturn interface on my business object?
Looks like there's no way not to use IReturn if you don't want to provide a URL to the JsonServiceClient Get() requests. Just decided to create another set of DTOs in my ServiceStack implementation, that are essentially mirrors of the real DTOs in another library. Then when a request comes in to my SS DTO, I create the other library's DTO, set each property, and pass it along.
Not pretty, but that's the best I could find so far.
I had the same problem using IReturn and Routes, as I wanted to use the DTOs
in assemblies with business logic, without ServiceStack references.
It worked for me, using in the Client Model
public class TestRequest
{
public int vendorId {get; set; }
public string barcode {get; set; }
public string username { get; set; }
public string password { get; set; }
}
then in the AppHost
Routes.Add<TestRequest( "/TestAPI/Reservation/{vendorId}/{barcode}"," GET,OPTIONS")
.Add<TestRequest>("/TestAPI/Reservation", "POST, OPTIONS")
and the call for JsonServiceClient with POST
request.vendorId=12344;
request.barcode="AAS1223";
TestResponse response = client.Post<TestResponse>(server_ip + "/TestAPI/Reservation", request);
OR with GET
TestResponse response = client.Get<TestResponse>(server_ip + "/TestAPI/Reservation/12344/AAS1223?username=John&password=99");
Then in the service Get or Post functions
public TestResponse Get(TestRequest request)
{
// request members hold the values of the url.
return DoBusinessLayerWork(request);
}
Using the Send() method from the JsonServiceClient type is the way to go about doing this.