Using multiple namespaces in WCF - c#

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

Related

Why I got null parameter in wcf service call if I change the Message tag name?

Actually this issue continued with my previous question that's about SOAP message serialization doesn't allow to change the root name via XmlRoot, so I manually change the root name of the message by WCF message inspector (via IClientMessageInspector), here is the new problem that I have trapped.
[ServiceContract]
public interface ICustomerService
{
[OperationContract]
bool EvaluateCustomer(Customer customer);
}
public class CustomerService : ICustomerService
{
public bool EvaluateCustomer(Customer customer)
{
// Evaluation is here ...
}
}
public class Customer
{
public Customer() { }
public Customer(string id, string name, int age)
{
ID = id;
Name = name;
Age = age;
}
public string ID { get; set; }
public String Name { get; set; }
public int Age { get; set; }
}
and the client message inspector is implemented to revise the message. Please be noted that the problem is caused by the changing of the message tag:
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MemoryStream ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms);
request.WriteMessage(xw);
xw.Flush();
ms.Position = 0;
string body = Encoding.UTF8.GetString(ms.ToArray());
xw.Close();
// There will be no problem if no change with the tag
body = body.Replace("<customer", "<MyCustomer");
body = body.Replace("</customer>", "</MyCustomer>");
ms = new MemoryStream(Encoding.UTF8.GetBytes(body));
XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(ms, new XmlDictionaryReaderQuotas());
Message newMessage = Message.CreateMessage(xdr, int.MaxValue, request.Version);
newMessage.Properties.CopyProperties(request.Properties);
request = newMessage;
return null;
}
everything is fine until here and the passed in request message is as below:
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/ITestService/EvaluateCustomer</a:Action>
<a:MessageID>urn:uuid:b35ab229-129a-469b-9a54-be3eaae53e3a</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
</s:Header>
<s:Body>
<EvaluateCustomer xmlns="http://tempuri.org/">
<customer xmlns:d4p1="http://schemas.datacontract.org/2004/07/WcfTest" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Age>43</d4p1:Age>
<d4p1:ID>No2</d4p1:ID>
<d4p1:Name>Jones</d4p1:Name>
</customer>
</EvaluateCustomer>
</s:Body>
</s:Envelope>
what I need is to change the <customer> tag and this is what BeforeSendRequest did. But when I break the call in the service operation, EvaluateCustomer, I found the parameter customer is null instead of a new customer instance, why? I ever implemented Customer class with IXmlSerializable and found that the ReadXml wasn't called if the class root tag was changed in SOAP message, why? And How I can implement the scenario so that the ReadXml can be called or the new passed-in service parameter (customer in above example) will no longer be null?
I tested above issue with ServiceHost and WSHttpBinding, wcf client is created with ChannelFactory, and message inspector is extended with Endpoint behavior.
Any hints is welcome and appreciated.
I believe you need to first decorate the 'Customer' class with a [DataContract] attribute. Setting the 'Name' part of that attribute to 'MyCustomer'.
Also remember to add [DataMember] to those members that are to be serialized.

how to format xml into soap response format?

I need to format an xml into a soap1.1 response envelope.
First, my business objects. I am aware of the insane nesting. I am not allowed to change the structure:
[DataContract]
public class DIVSReturnObject
{
public CoverageResponseDocument coverageResponseDoc { get; set; }
public DIVSReturnObject()
{
coverageResponseDoc = new CoverageResponseDocument();
}
}
[DataContract]
public class CoverageResponseDocument
{
public Detail detail { get; set; }
public CoverageResponseDocument()
{
detail = new Detail();
}
}
[DataContract]
public class Detail
{
public PolicyInformation policyInformation { get; set; }
public VehileInformation vehicleInformation { get; set; }
public Detail()
{
policyInformation = new PolicyInformation();
}
}
[DataContract]
public class PolicyInformation
{
public OrganizationDetails organizationDetails { get; set; }
public PolicyDetails policyDetails { get; set; }
public CoverageStatus coverageStatus { get; set; }
public PolicyInformation()
{
coverageStatus = new CoverageStatus();
}
}
[DataContract]
public class CoverageStatus
{
public ResponseDetails responseDetails { get; set; }
public CoverageStatus()
{
responseDetails = new ResponseDetails();
}
}
[DataContract]
public class ResponseDetails
{
[DataMember]
[XmlAttribute(AttributeName = "ResponseCode")]
public string ResponseCode { get; set; }
[DataMember]
[XmlAttribute(AttributeName = "UnconfirmedReasonCode")]
public string UnconfirmedReasonCode { get; set; }
}
Next, my code to serialize the desired object to XML:
XmlDocument xmlDoc = new XmlDocument();
XmlSerializer xmlSerializer = new XmlSerializer(divsResponse.GetType());
using (MemoryStream xmlStream = new MemoryStream())
{
xmlSerializer.Serialize(xmlStream, divsResponse);
xmlStream.Position = 0;
xmlDoc.Load(xmlStream);
return xmlDoc.InnerXml;
}
Next, the resulting XML string. Note that ResponseCode and UnconfirmedReasonCode are attributes when they should be their own elements:
<?xml version="1.0"?>
<DIVSReturnObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<coverageResponseDoc>
<detail>
<policyInformation>
<coverageStatus>
<responseDetails ResponseCode="Unconfirmed" UnconfirmedReasonCode="VIN1" />
</coverageStatus>
</policyInformation>
</detail>
</coverageResponseDoc>
</DIVSReturnObject>
Finally, the desired envelope with the correct namespaces (how do I add those?):
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<CoverageResponseDocument PublicationVersion="00200809" PublicationDate="2008-11-05" xmlns="http://www.iicmva.com/CoverageVerification/">
<Detail>
<PolicyInformation>
<CoverageStatus>
<ResponseDetails>
<ResponseCode>Unconfirmed</ResponseCode>
<UnconfirmedReasonCode>VIN1</UnconfirmedReasonCode>
</ResponseDetails>
</CoverageStatus>
<OrganizationDetails>
<NAIC>12345</NAIC>
<!-- this can be echoed from the request or provide the actual NAIC that has evidence of coverage -->
</OrganizationDetails>
<PolicyDetails>
<!-- this section can be echoed from the request -->
<VerificationDate>2015-01-01T00:00:00.000</VerificationDate>
<PolicyKey>UNKNOWN</PolicyKey>
<PolicyState>CT</PolicyState>
</PolicyDetails>
</PolicyInformation>
</Detail>
</CoverageResponseDocument>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Under the following conditions which I hope apply in your case, there is indeed a pretty quick way to solve your problems. And time is money, right?
Soap response is rather static (no collection etc. Just a hand full of fields to fill in).
You do not have full control over your code base (DOM) and as such look to decouple your solution somewhat from the DOM code. With DOM I mean the data model classes you showed in your question.
The drawbacks of the solution I show below are:
Not a "generic" solution which should be applied in all situations. (suits your current problem but maybe not your next ones).
As cool as text templating is, there is one source of possible errors, once things get more complicated (dynamic) - it is hard to proof that you will produce the correct output at all times. Maybe then, other solutions such as a reviewable xslt transform or even better a "contract first -> code generating of the DOM" appraoch is more reliable.
I suggest you warm yourself up a little before you add this to your main project. A small C# console application project will do.
Create your toy console app (File-> New Project -> C# -> ConsoleApp).
Right click your project in solution explore and Add New Item -> Runtime Text Template. It is important to use "Runtime Text Template" and not "Text Template" in your case. Give it a proper name (I used in my demo code "MySoapResponse.tt".
In the generated .tt file which is now part of your project items, copy and paste the desired soap output below what is already contained in the file.
Create yourself a dummy DOM just to have the same scenario as you have it in your application.
Add a new C# code file to your project and add a partical class in the same namespace as the code-behind .tt code uses, with the name of the generated template class (which is partial). Add a constructor where you pass as argument the instance of your DOM which you want to use for generating the message.
Create a few Query functions or simply a Document {get;} property.
In your console app main, create an instance of your DOM with some data in it.
Also create an instance of your generated generator object.
Call the TransformText() method and save the string to a file on your disk for inspection.
Rince and repeat: Modify your .tt template by adding more and more markdown statements at the spots where you need the data from your DOM.
As you can see - 10 steps and you are done.
This is how the transformation code looks like (even simpler than XmlSerializer calls):
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
MyDOM.Root document = MyDOM.Root.CreateExampleDocument();
MySoapResponse responseGenerator = new MySoapResponse(document);
System.IO.File.WriteAllText(#"E:\temp\blabla.txt", responseGenerator.TransformText());
System.Console.WriteLine("Done transforming!");
}
}
}
And this - just for completness sake is my miniDOM I invented for this answer:
namespace MyDOM
{
internal class Root
{
internal ChildA childA { get; set; }
internal ChildB childB { get; set; }
internal static Root CreateExampleDocument()
{
Root r = new Root();
r.childA = new ChildA();
r.childB = new ChildB();
r.childA.ResponseCode = "I have my reasons!";
r.childA.ReasonCode = "I have response code using T4!";
return r;
}
}
internal class ChildA
{
internal string ResponseCode { get; set; }
internal string ReasonCode { get; set; }
}
internal class ChildB
{
}
}
I only added 2 items - which is sufficient to showcase how it all works.
In the .tt file of course, I also added just enough to cover those 2 items.
<## template language="C#" #>
<## assembly name="System.Core" #>
<## import namespace="System.Linq" #>
<## import namespace="System.Text" #>
<## import namespace="System.Collections.Generic" #>
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<CoverageResponseDocument PublicationVersion="00200809" PublicationDate="2008-11-05" xmlns="http://www.iicmva.com/CoverageVerification/">
<Detail>
<PolicyInformation>
<CoverageStatus>
<ResponseDetails>
<ResponseCode><#=ResponseCode()#></ResponseCode>
<UnconfirmedReasonCode><#=ReasonCode()#></UnconfirmedReasonCode>
</ResponseDetails>
</CoverageStatus>
<OrganizationDetails>
<NAIC>12345</NAIC>
<!-- this can be echoed from the request or provide the actual NAIC that has evidence of coverage -->
</OrganizationDetails>
<PolicyDetails>
<!-- this section can be echoed from the request -->
<VerificationDate>2015-01-01T00:00:00.000</VerificationDate>
<PolicyKey>UNKNOWN</PolicyKey>
<PolicyState>CT</PolicyState>
</PolicyDetails>
</PolicyInformation>
</Detail>
</CoverageResponseDocument>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The calls I invoke from within the .tt template (e.g. <#=ResponseCode()) are methods I added to my partial class part of the generator object.
namespace ConsoleApplication2
{
public partial class MySoapResponse
{
MyDOM.Root Document { get; }
internal MySoapResponse(MyDOM.Root document)
{
Document = document;
}
internal string ResponseCode()
{
return Document.childA.ResponseCode;
}
internal string ReasonCode()
{
return Document.childA.ReasonCode;
}
}
}
And that is about it. A bit of clicking and a few lines of code actually written (let it be 50 lines). For sure better than using a StringBuilder by hand.

Service Reference generates an incorrect proxy for ServiceStack

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.

Sending Custom Objects to an asp.net webservice

So I want to send a custom object to an asp.net 4.0 Webservice.
I have been sending a bunch of individual parameters but i want to start reusing some code so I'd like to do something like
[WebMethod(Description = "Testing Sending Object In")]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public bool COT(CodeWithMessage CWM)
{
...
where the class for CodeWithMessage is
namespace UserSite
{
namespace General
{
public class CodeWithMessage
{
public int iCode { get; set; }
public string sMessage { get; set; }
public CodeWithMessage()
{
}
}
}
}
The webservice defines the input as
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<COT xmlns="http://localhost">
<CWM>
<iCode>int</iCode>
<sMessage>string</sMessage>
</CWM>
</COT>
</soap:Body>
</soap:Envelope>
I tried passing the webservice the following json
var postdata = { "iCode": -5, "sMessage": "BlogName " }
I get back an Internal Server Error [500]
{"Message":"Invalid JSON primitive: iCode."
Do I need to wrap this up somehow to indicate it is part of this object? I tried
{"__type":"UserSite.General.CodeWithMessage","iCode":-5,"sMessage":"Blog Name Already Exists"}
Which is what CodeWithMessage returns from another webservice that sends it instead of receiving it.
Your error says that you Json is wrong you need to check the json string you are passing to the webservice.
its may be like
var postdata = { 'iCode': '-5', 'sMessage': 'BlogName' }
check the jSon String example : http://www.json.org/js.html on jSon site........

How can I Serialize Properly

If I have a class MovieClass as
[XmlRoot("MovieClass")]
public class Movie
{
[XmlElement("Novie")]
public string Title;
[XmlElement("Rating")]
public int rating;
}
How can I've an attribute "x:uid" in my "Movie" element, so that the output when XmlSerializer XmlSerializer s = new XmlSerializer(typeof(MovieClass)) was used
is like this:
<?xml version="1.0" encoding="utf-16"?>
<MovieClass>
<Movie x:uid="123">Armagedon</Movie>
</MovieClass>
and not like this
<?xml version="1.0" encoding="utf-16"?>
<MovieClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie x:uid="123" Title="Armagedon"/>
</MovieClass>
Note: I want the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" removed, if possible.
I answered this in your original post, but I think this one is worded better so I will post it here as well, if it gets closed as duplicate you can modify your original post to mirror this question.
I don't think this is possible without having Title be a custom type or explicitly implementing serialization methods.
You could do a custom class like so..
class MovieTitle
{
[XmlText]
public string Title { get; set; }
[XmlAttribute(Namespace="http://www.myxmlnamespace.com")]
public string uid { get; set; }
public override ToString() { return Title; }
}
[XmlRoot("MovieClass")]
public class Movie
{
[XmlElement("Movie")]
public MovieTitle Title;
}
which will produce:
<MovieClass xmlns:x="http://www.myxmlnamespace.com">
<Movie x:uid="movie_001">Armagedon</Movie>
</MovieClass>
Although the serializer will compensate for unknown namespaces with a result you probably won't expect.
You can avoid the wierd behavior by declaring your namespaces and providing the object to the serializer..
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("x", "http://www.myxmlnamespace.com");
It's not valid XML if you don't have x declared as a namespace prefix. Quintin's response tells you how to get valid XML.

Categories

Resources