Some Web Service DTO questions

Some Web Service DTO questions

Old forum URL: forums.lhotka.net/forums/t/8891.aspx


ltgrady posted on Thursday, May 06, 2010

We have an app using an older version of CSLA.  We've been working mostly with asp.net forms and we've done some web service work but they're not integrated as much as we would like.  We're working on a new module of the system and we need to expose the same methods via web service as we provide in our forms.  So I'm trying to keep as much code in the BL as I can and integrate/re-use code a lot more than we have previously.

Our web services all use DTO parameters, unless there is only 1 or 2 parameters.  So if I have a method called addOrder there would be addOrder(dtoOrderItem) and dtoOrderItem will hold all of the parameters for the order we're trying to create.

A few questions....

1.  In the past in my web service .cs file i've created an instance of the OrderItem.cs CSLA BO and then looped through dtoOrderItem plugging the values from one to the other. Then in the asp.net form I just create the CSLA BO and load it with form data and save it.    However, now that I'd like to create a single AddOrder item for both forms and services I'm wondering if I should just pass the DTO straight into AddOrder BL method and load a DTO in the forms code behind and pass it into the same object.  This would centralize the code, however I'm afraid it adds complexity when dealing with required fields and error reporting.

2.  Is there a better way to transfer data from DTO to CSLA BO than looping through and mapping properties  BO.Name = DTO.Name.  Especially when i pass in collections, like AddOrders(dtoOrderItemCollection) I'm wondering if looping through the collection and assigning every property across objects is the most efficient method.  Maybe I'm missing something very obvious.

3.  How do you deal with required fields and data validation in DTO's.  There is no code allowed.  So if I have a web service to add an order and 5 of the properties are required in the dtoOrderItem DTO and 3 aren't, how do I enforce that validation?  Just return an error in the service if they don't provide it?   I thought there may be some way to include some kind of validation specification in the wsdl, but I don't think there is.

Thanks for any help or advice you can provide.

 

 

 

RockfordLhotka replied on Thursday, May 06, 2010

To maintain decoupling and separation of layers, the business object should be unaware of the DTO types. So your existing model is correct.

If you think about the service as being an interface, just like a web or Windows interface, then making the business type aware of the DTO type is like making the business type aware of your web form - total violation of the layer boundary.

You can use Csla.Data.DataMapper to reduce the mapping code, but the mapping still really needs to happen.

In terms of validation, as the author of a service you are defining an interface that is used by your consumers (whoever they are). Your interface consists of:

Think about it this way. When a user interacts with your Windows Forms UI and they type something into the form, there are really two possible results: success (the form accepts the value) and failure (the form indicates to the user that the value is bad).

When a consumer calls a service there are really two possible results: success or failure. Both are part of the contract.

In WCF the concept of a fault contract is a first-class citizen. In asmx that's not true, but the idea remains valid. Failure shouldn't result in an exception - it should result in the service returning a failure result as part of its overall contract.

Consider a service operation that returns a value:

public MyResult MyOperation(MyRequest request)

That operation might succeed - returning a valid MyResult. But it might fail too - for numerous reasons.

In asmx, MyResult should be able to express failure as well as success.

In WCF MyResult can express just success, and you can attach a formalized fault to the operation (using an attribute), and the fault can return a different data contract (DTO) containing the failure informaiton.

The point being that success and failure are both formally part of your contract - but the implementations vary a bit depending on the technology you are using.

The trick with WCF is that most consumers (other than WCF on the client) can't handle faults. They are part of the WS-* specs, but few service stacks handle them. So the asmx model is the only one that works universally....

ltgrady replied on Thursday, May 06, 2010

Understanding it like that makes quite a bit more sense to me than how I was thinking.  Practically speaking my agreement with people using my web service is that I'm either going to provide the expected result set if what they submitted to me was valid and successful or I'm going to provide a separate response if what they've submitted was invalid and caused a failure.

My question at this point is applying it in my situation.  If I am specifying a specific return object type, how do I return a failure object instead?

Stripped down example of my code 

    [System.Xml.Serialization.XmlInclude(typeof(dtoOrderItem))]
    [SoapHeader("Credentials")]
    public dtoOrderItem getOrder(Guid orderID)

    {
        Login();
        dtoOrderItem oi = Order_GetOrder(orderID);
        return oi;
    }

if the Order did not exist or there was some error I would want to return a dtoFailure object
 <dtoFailure>
   <errorMsg> 
          Error does not exist for the ID you provided
  </errorMsg>
 </dtoFailure> 

How do I return that when I've specified dtoOrderItem as the return type?

 

Also, in the case of adding an order where the dtoOrderItemAdd object has 10 fields, I understand the concept that validation happens on my end and I return back either a success (a new Order ID) or a failure (dtoFailure object).  However, is there some vehicle to define for the person consuming my service that the 4 of those 10 fields are required?  Does it become trial and error for them otherwise to submit items and keep getting back errors until they realize Field A is required, Field B can't be longer than 30 characters, Field C needs to be a date?  Is that just a question of documentation outside of the wsdl?

RockfordLhotka replied on Thursday, May 06, 2010

I tend to define my return value data contract like this:

public class OrderitemResult
{
  public dtoOrderItem { get; set; }
  public ErrorInfo { get; set; }
}

That's what gets returned from the service. If the service succeeds, ErrorInfo is null, and dtoOrderItem is not. If the service fails then ErrorInfo is not null and dtoOrderItem is null.

This opens up for the answer to the second part of your question - which is how to handle more complex error results - since "ErrorInfo" can be an arbitrarily complex DTO type - even a list of success/fail results if you'd like.

ltgrady replied on Thursday, May 06, 2010

That's a great solution, thanks Rocky.

Copyright (c) Marimer LLC