Service Reference generates an incorrect proxy for ServiceStack - c#

I have a service stack service with the following request and response classes in the same namespace
public class GetContactMasterData
{
}
public class GetContactMasterDataResponse
{
public IdDescription[] EntityTypes { get; set; }
public IdDescription[] NameTypes { get; set; }
public IdDescription[] AddressTypes { get; set; }
public ResponseStatus MyResponseStatus { get; set; }
}
I tested the service successfully using soapUI. This is the response
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Body>
<GetContactMasterDataResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.servicestack.net/types">
<AddressTypes>
<IdDescription>
<Description>Home</Description>
<Id>1</Id>
</IdDescription>
...
</AddressTypes>
<EntityTypes>
<IdDescription>
<Description>Corporation</Description>
<Id>1</Id>
</IdDescription>
...
</EntityTypes>
<MyResponseStatus i:nil="true" />
<NameTypes>
<IdDescription>
<Id>4</Id>
<Description>Other</Description>
</IdDescription>
...
</NameTypes>
</GetContactMasterDataResponse>
</s:Body>
</s:Envelope>
When I create a console app to test this service the service reference generates a proxy object. This is how intellisense guides you to call the GetContactMasterData method
GetContactMasterData(out IdDescription[], out ResponseStatus myResponseStatus, out IdDescription[] NameTypes):IdDescription[] addressTypes
My question is:
Why do EntityTypes and NameTypes become out parameters vs addressTypes becomes the return type of the method?

Included in ServiceStack's SOAP Support wiki are limitations to be mindful of with SOAP:
Since VS.NET's Add Service Reference is optimized for consuming .asmx
or WCF RPC method calls it doesn't properly support multiple return
values (e.g. when you also want a ResponseStatus property) where it
will generate an ugly proxy API complete with out parameters.
If you want to ensure a pretty proxy is generated you should only have
1 first-level property which contains all the data you want to return.

Related

Using multiple namespaces in WCF

I have an application in C# Then i have to give a requested result as below.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Body>
<MyAppResponse xmlns="http://www.specificAddress.com">
<MyAppResult xmlns:abc1="http://schemas.specificAddress.Responses" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<abc1:ResponseData xmlns:aef2="http://schemas.datacontract.org/Test.GenericMerchantServices.Classes.Domain">
<aef2:ResponseCode>0000</aef2:ResponseCode>
<aef2:ResponseDescription>Successful</aef2:ResponseDescription>
</abc1:ResponseData>
</MyAppResult>
</MyAppResponse>
</s:Body>
</s:Envelope>
In this case i am using a .NET 5 app and soapCore to work with soap in .NET Core. Here ise my interface and derived class.
[ServiceContract]
public interface IMyAppService
{
[OperationContract]
MyAppResult MyApp(MyAppRequest request);
}
public class MyAppService : IMyAppService
{
public MyAppResult MyApp(MyAppRequest request)
{
return new MyAppResult()
{
ResponseData = new ResponseData()
{
ResponseCode = "0000",
ResponseDescription = "Test "
}
};
}
}
I've tried to update namespaces with data annotiations but i don't know how can i update prefix and make response as requested. Here is my class
[XmlRoot(Namespace = "http://schemas.specificAddress.Responses")]
public class MyAppResult
{
[XmlElement(ElementName = "ResponseData", Namespace = "http://schemas.datacontract.org/Test.GenericMerchantServices.Classes.Domain")]
public ResponseData ResponseData { get; set; }
}
public class ResponseData
{
public string ResponseCode { get; set; }
public string ResponseDescription { get; set; }
}
When i use this endpoint i get this result.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Body>
<MyAppResponse xmlns="http://tempuri.org/">
<MyAppResult xmlns="http://schemas.specificAddress.Responses" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ResponseData xmlns="http://schemas.datacontract.org/Test.GenericMerchantServices.Classes.Domain">
<ResponseCode>0000</ResponseCode>
<ResponseDescription>Test </ResponseDescription>
</ResponseData>
</MyAppResult>
</MyAppResponse>
</s:Body>
</s:Envelope>
The question is how can i add ns and custom prefix to my project and how can i add custom namespace on myappresponse.
To change the namespace of a WCF service, you need to apply the Namespace attribute to the ServiceContractAttribute on the service contract interface. Details can be found at http://blog.rebuildall.net/2010/11/10/WCF_service_namespaces
About adding prefixes you can take a look at this document.
XML Serialization and namespace prefixes
C# WCF Global Namespaces - Royal Mail

XML&WCF POST request: some parameters are read, other not

I have a wcf restful service on a IIS server.
I have made some API, which can be called sending both xml or json.
I've made my C# classes and then, I'm testing it. With JSON is perfect, but I have still some issues with XML request.
I want to send the xml with a post and this is the xml I send:
<?xml version="1.0" encoding="utf-8" ?>
<SetClientiXML
xmlns="http://tempuri.org/">
<dati>
<ArrayOfWrapClienti
xmlns="http://schemas.datacontract.org/2004/07/MultipayOnline"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<WrapClienti>
<CODRETE>0018</CODRETE>
<CODICE>20685</CODICE>
<NOME>A.T.E.R. Azienda Territoriale</NOME>
<INDIRIZZO>PIAZZA POZZA</INDIRIZZO>
<CITTA>Verona</CITTA>
<CAP>37123</CAP>
<PROV>VR</PROV>
<CODICEFISCALE>00223640236</CODICEFISCALE>
<PIVA>223640236</PIVA>
<EMAIL/>
<ESPOSIZ_CONTABILE>937,02</ESPOSIZ_CONTABILE>
<STATO>FALSE</STATO>
</WrapClienti>
</ArrayOfWrapClienti>
</dati>
<retista>3303903</retista>
<hashedString>oklkokokokok</hashedString>
</SetClientiXML>
the wcf read well "retista" and "hashedString", but "dati" is empty (0 elements), while I expect it has got the "wrapClienti" object I sent.
This is the prototype of my API:
[OperationContract]
[WebInvoke(UriTemplate = "SetClienti.xml", Method = "POST", BodyStyle=WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Xml)]
GenericResponse SetClientiXML(List<WrapClienti> dati, string retista, string hashedString);
So, the problem is that the List is empty.. why? How can I write the xml to make readable the list?
Ask me if I can give to you more details.
UPDATE: More weird!!
With this xml:
<?xml version="1.0" encoding="utf-8" ?><SetClientiXML xmlns="http://tempuri.org/">
<dati>
<WrapClienti xmlns="http://schemas.datacontract.org/2004/07/MultipayOnline" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CODRETE>0018</CODRETE>
<CODICE>20685</CODICE>
<NOME>A.T.E.R. Azienda Territoriale</NOME>
<INDIRIZZO>PIAZZA POZZA</INDIRIZZO>
<CITTA>Verona</CITTA>
<CAP>37123</CAP>
<PROV>VR</PROV>
<CODICEFISCALE>00223640236</CODICEFISCALE>
<PIVA>223640236</PIVA>
<EMAIL/>
<ESPOSIZ_CONTABILE>937,02</ESPOSIZ_CONTABILE>
<STATO>FALSE</STATO>
</WrapClienti>
</dati>
<retista>3303903</retista>
<hashedString>oklkokokokok</hashedString>
</SetClientiXML>
the wcf read some attributes of the List, and other.. are nul!!!
I my WrapClienti I have a lof of attributes. Two of them are:
private string p_CAP { get; set; }
public string CAP
{
get
{
if (model == null)
return p_CAP.ToSafeString();
else
return this.model.CAP.ToSafeString();
}
set { p_CAP = value; }
}
private string p_PROV { get; set; }
public string PROV
{
get
{
if (model == null)
return p_PROV.ToSafeString();
else
return this.model.PROV.ToSafeString();
}
set { p_PROV = value; }
}
the problem is, with the xml above and with two breakpoint on the two set methods, only the set of PROV is called and, the one of CAP, not!!! Why? Now I'm really getting crazy... why this behavior??
Solution here.
This has to do with the ordering of the fields in your XML. It sounds very strange, but WCF DataContractSerializer is really fussy about the order in which the fields are encountered in the XML, but even worse, also in comparison to how they are defined in the code.
You see, the serializer wants the fields to be defined in alphabetical order, and if you serialize an instance of your class, you will find that the resulting XML fields are in alphabetical order. However, on deserialization, the serializer finds that the type you want to deserialize to has the fields defined in the "wrong" order. In this situation the behavior can seem random, but I think it has something to do with the fact that CAP should be the first field encountered, whereas PROV should be the last field, alphabetically.
So you have two options:
Reorder your XML and the fields in your class to be in alphabetical order, or
Decorate your class members with the DataMemeber property, and define the order of serialization.
You can do 2 like this:
[DataContract]
public class WrapClienti
{
[DataMember(Order=1)]
public string CAP { get; set; }
[DataMember(Order=2)]
public string PROV { get; set; }
...etc
}

WCF Return a custom result with an object type

I'm trying return an object from my WCF function call, and if I specify the type in my ServiceModel, the result is sent successfully.
But if I send an object displayme a message like:
This operation is not supported in the wcf test client because it uses type
Code:
[DataContract]
public class ServiceModel
{
[DataMember]
public int status { get; set; }
[DataMember]
public string message { get; set; }
[DataMember]
public requestdVM result { get; set; }
//public object result { get; set; }
}
The problem is similar to: WCF service: Returning custom objects but as solution they change the type
In common there is no such possibility as using base common objects in WCF services.
Here is the example of WSDL type declaration:
<complexType name="TimePeriod">
<all>
<element name="StartTime" type="xsd:timeInstant"/>
<element name="EndTime" type="xsd:timeInstant"/>
</all>
</complexType>
This schema is visible for the clients and then .NET can understand that it can use his own object and transfer it.
In .NET this will be similar to
[DataContract]
public class TimePeriod
{
[DataMember]
public DateTime StartTime {get;set;}
[DataMember]
public DateTime EndTime {get;set;}
}
Java can understand WSDL and use own class and objects; Python can do it, PHP can do it, hope you got the SOA idea.
Now you want to send object that can be anything. It is not possible to do with contracts.
However if you can promise that only .NET is using in your project then there is a solution.
The idea is to serialize .NET object in binary format and give a type name of the object. Then on the client side deserialize it. Of course you cannot simply do it with sending object from .NET to, say, Python.
1) Small object sending in string XML format
[Serializable]
public class Data
{
public string TypeName {get;set;} // typeof(string), typeof(int), etc.
public string Xml {get;set;} // serialized object via XmlSerializer
}
Then after serialization and receiving object in the client it can be deserialized
Data objectData;// we got it from service
var serializer = new XmlSerializer(typeof(objectData.TypeName));
object result;
using (TextReader reader = new StringReader(objectData.Xml))
{
result = serializer.Deserialize(reader);
}
Also binary serialization can be used instead of XML, then there will be byte[] property.
2) Second solution will be using binary stream and write type name in the header of message, the approach is similar to the first point.
All depends on situation, but I recommend it for big files (> 1 MB).

System.Runtime.Serialization.SerializationException on a string value

I have a WCF REST service that is returning a business object that contains a string field called 'GeoDataKML'. This field is just a string value, but it has KML string inside it (encoded, so that it doesn't interfere with XML serialization of this business object). The problem is that when this field has data in it, i get a WCF error trying to download this data:
System.Runtime.Serialization.SerializationException
Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
The data i'm trying to send is below. If i blank out the GeoDataKML value, then the service works fine, no errors. Applying KnownType doesn't seem to do any good. Any ideas on how to get WCF REST to NOT try to serialize the data in this string object?
<?xml version="1.0" encoding="Windows-1252"?><ArrayOfFullLogEntry xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FullLogEntry>
<IsSelected>false</IsSelected>
<StaticGUID>be547685-2bdd-4abf-84a4-d20c13c5fe58</StaticGUID>
<GeoDataKML><?xml version="1.0" encoding="Windows-1252"?><kml xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/kml/2.2"><Placemark id="7e5272b6-d1b4-4128-95bf-24ea9358681a"><name>CasualtyTool</name><styleUrl>#CasualtyTool</styleUrl><Point><altitudeMode>clampToGround</altitudeMode><coordinates>-115.594205424414,41.295597016765,5000</coordinates></Point></Placemark></kml></GeoDataKML>
<SymbolGUID>00000000-0000-0000-0000-000000000000</SymbolGUID>
<GeoDataTypeGUID>00000000-0000-0000-0000-000000000000</GeoDataTypeGUID>....
FullLogEntry only has these values:
public class FullLogEntry
{
public Guid StaticGUID {get;set;}
public string GeoDataKML {get;set;}
public Guid GeoDataTypeGUID{get;set;}
public Guid SymbolGUID{get;set;}
public bool IsSelected {get;set;}
}
service contract is just:
[ServiceContract(Namespace = WCFServiceBaseClass.ServiceNamespace)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, Namespace = WCFServiceBaseClass.ServiceNamespace)]
where the namespace is 'http://test.org'
needed to add
<System.Runtime.Serialization.KnownType(GetType(PlacemarkType))> _
at the top of the fulllogentry class. works now!

Not working maxlengthurl(too longer) in any browser

I am looking for new wcf rest web service in asp.net 4.0 using vs2010.
here is my code for passing url:
"homewizard/Service1.svc/monthlytips/country_state=US_IL_N/tip_code=NoInformation/feature=ACC,FAHD,WHG,FP,WA,DY,DWSH,GD,REF,STV,OVN,MW,CPTR,ATT,ROOF,RG,BSMT,FDN,SPX,GAR,EGF,PLB,DOOR,WIND,WS,LWN,DKG,PF,BBQ,WSD,OWF,DWY,OLIT,HL,SPTC,CF,WF,CPTS,DVB,FURW,FURL,FURU,MAT,BATH,KITC,CLST,LITE,SD,COD,FE,EMS,PC,SS,MED,EAUD,ENR,GARR,INR,MGR,TAXR,TELR,CGD,DOOR,WIND,WS/dwelling_type=1/tip_priority=1/month=3/tip_knowledge_level=1/tipbr_ensav=0/tipbr_safe=0/tipbr_avoid=1/tipbr_comfort=1/tipbr_value=1/tipbr_appear=1/tipbr_green=0/tipbr_money=0/tipbr_space=1/tipbr_allergy=2/tipbr_elderly=2/tipbr_children=2/tip_location_temp=0/tip_location_humidity=0"
output:Bad Request - Invalid URL HTTP Error 400. The request URL is invalid.
my web config is: httpRuntime maxUrlLength="1024"
but it's working my local host not in server pc.
thanks in advance.
To be honest your issue is that that's not a restfull URL, the url should just be the location of the resource you are dealing with, the body of the request should contain the details of the request (as xml or json for example). It will both solve your problem and result in less code to manage the parameters via deserialization into a request object server side, not to mention clear up that url.
UPDATE
Are you sure thats a GET and not a PUT, what is the call meant to do?
If it is a GET I'd do something like the following...
Service Interface
[WebGet(UriTemplate = "monthlytips")]
AppropriateReturnType MonthlyTips(MonthlyTip monthlytip);
DTO Object
public class MonthlyTip
{
public string CountryState { get; set; }
public string TipCode { get; set; }
public List<string> Feature { get; set; }
public int DwellingType { get; set; }
public int TipPriority { get; set; }
...
...
}
Thats off the top of my head so it might need a little refining and you'll need to implement the interface, finish the DTO and so on but that's the approach you should take.

Categories

Resources