Can you configure default settings for the XmlWriter used by WCF? - wcf

Is there any way to configure the default XmlWriter used by WCF service with DataContractSerializer when serializing data?
Out of the box WCF service using DataContractSerializer is losing the new lines (\r\n).
[Edit: I apologize for the confusion. Out of the box WCF DOES NOT lose the new lines.]
I am able to make XmlWriter encode the new lines to 
 by using XmlWriterSettings (NewLineHandling.Entitize), but I want to make WCF behave the same way when serialize my object.
public string Serialize<T>(T object)
{
var serializer = new DataContractSerializer(typeof(T));
using (var stringWriter = new StringWriter())
{
var settings = new XmlWriterSettings { NewLineHandling = NewLineHandling.Entitize };
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
serializer.WriteObject(xmlWriter, object);
string xml = stringWriter.ToString();
return xml;
}
}
}

If you want to use a different XmlWriter, you'll need to use a custom message encoder. The sample at http://msdn.microsoft.com/en-us/library/ms751486.aspx shows how you could write one.
But I've never seen WCF losing the \r\n characters - it properly entitizes the \r into 
, at least all the times I checked. When I run the code below it shows the characters properly returned:
public class StackOverflow_12205872
{
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
string str = proxy.Echo("Hello\r\nworld");
Console.WriteLine(str);
Console.WriteLine(str[5] == '\r' && str[6] == '\n');
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
And Fiddler shows this request being sent:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Header></s:Header><s:Body><Echo xmlns="http://tempuri.org/"><text>Hello
world</text></Echo></s:Body></s:Envelope>
The response also contains the entitized CR character. Can you share more details about your configuration (including binding)?

Related

How to create single instance for multiple endpopints in wcf

Scenario:
I have single instance type of wcf service. which keep data in memory.
Clients call the service and get data without hitting the db.
Wcf Service is host in windows service.
In windows service i have used timer, on the bases of which wcf service hit the database and refresh the data.
Now problem is that when call of refresh take place, request of data client impacted and got time out.
Now i have tried two endpoints. One for consumer and second for refresh.
Both working well, but even refresh endpoint refresh the data. consumer endpoint gives old data whether refresh endpoint showing correct data.
Code Example
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service1 : IService1
{
List<string> Name;
public Service1()
{
Name = new List<string>();
CollectData1();
}
public List<string> CollectData()
{
CollectData1();
return Name;
}
public List<string> CollectData1()
{
List<string> _name = new List<string>();
string connectionString = ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString;
using (SqlConnection sqlConnection1 = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "select name from test";
cmd.Connection = sqlConnection1;
sqlConnection1.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
string a = Convert.ToString(reader[0]);
_name.Add(a);
}
}
}
}
}
Name = _name;
return Name;
}
public List<string> GetData()
{
return Name;
}
}

Returning XmlDocument from WCF service not working

I am trying to update some WCF service methods that return strings to return XmlDocument objects. I've tried returning it as-is and encapsulating it in a datacontract object. Either way I'm hitting an error upon attempting to update the service reference. The error suggest encapsulating it in a datacontract with an operations contract which I am doing. Is there a trick to this?
There's a way to return a XmlDocument from WCF, but you need to use the XmlSerializer instead of the default serializer (DataContractSerialier) - the code below shows how it can be done. Having said that, do consider using data transfer objects as mentioned in the comments, unless your scenario really requires a XmlDocument to be transferred.
public class StackOverflow_8951319
{
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
[OperationContract, XmlSerializerFormat]
XmlDocument GetDocument();
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
public XmlDocument GetDocument()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(#"<products>
<product id='1'>
<name>Bread</name>
</product>
<product id='2'>
<name>Milk</name>
</product>
<product id='3'>
<name>Coffee</name>
</product>
</products>");
return doc;
}
}
static Binding GetBinding()
{
var result = new WSHttpBinding(SecurityMode.None);
//Change binding settings here
return result;
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Echo("Hello"));
Console.WriteLine(proxy.GetDocument().OuterXml);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
If you want to be able to pass arbitrary XML on the wire the best way to do it is to use XElement rather than XmlDocument
XmlDocument isn't serializable

WCF Interoperability with other services, content type is text/html

I am trying to create a WCF client that operates with an http rest endpoint not based on Microsoft technologies. My WCF-foo is pretty weak, so I don't understand what I am doing incorrectly... I've created a service contract that looks like this...
[ServiceContract]
public interface IFilters
{
[OperationContract]
[WebGet(UriTemplate = "/api/filter.getavailable.xml?api_user={username}&api_key={password}")]
String GetAvailableFilters(String username, String password);
}
Which I try and run like this...
public String Run(String username, String password)
{
var binding = new BasicHttpBinding();
binding.MessageEncoding = WSMessageEncoding.Text;
binding.Security.Mode = BasicHttpSecurityMode.Transport;
var endpointAddress = new EndpointAddress("https://sendgrid.com");
IFilters proxy = ChannelFactory<IFilters>.CreateChannel(binding, endpointAddress);
var result = "";
using (proxy as IDisposable)
{
result = proxy.GetAvailableFilters(username, password);
}
return result;
}
When I run this code, I get an exception that says...
The content type text/html; charset=utf-8 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: ...
Now if I just try and access this from a web browser (with different credentials), I get the xml doc I'm looking for...
https://sendgrid.com/api/filter.getavailable.xml?api_user=foo&api_key=bar
what am I doing incorrectly?
Edit:
This was the final working solution...
public Filters Run(String username, String password)
{
var binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
var endpointAddress = new EndpointAddress("https://sendgrid.com");
var factory = new ChannelFactory<IFilters>(binding, endpointAddress);
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
var proxy = factory.CreateChannel();
using (proxy as IDisposable)
{
var results = proxy.GetAvailableFilters(username, password);
return results;
}
}
On the client side you are using BasicHttpBinding which is a SOAP binding not a REST binding. You should try using the WebClient class instead
http://msdn.microsoft.com/en-us/library/system.net.webclient.aspx
or WebChannelFactory
http://msdn.microsoft.com/en-us/library/bb908674.aspx

passing collection or array type input parameter wcf service

I have written a WCf Service which has a Collection type input body parameter and another parameter as query string as following:
[WebInvoke(Method = "PUT", UriTemplate = "users/role/{userID}",BodyStyle=WebMessageBodyStyle.WrappedRequest)]
[OperationContract]
public bool AssignUserRole(int userID,Collection<int> roleIDs)
{
//do something
return restult;
}
Now when I am trying to pass this parameter I am getting de serializing error. I have tried following format:
<AssignUserRole xmlns="http://tempuri.org/">
<roleIDs>
<roleID>7</roleID>
</roleIDs>
</AssignUserRole>
<AssignUserRole xmlns="http://tempuri.org/">
<ArrayOfroleID>
<roleID>7</roleID>
</ArrayOfroleID>
</AssignUserRole>
<AssignUserRole xmlns="http://tempuri.org/">
<ArrayOfint>
<int>7</int>
</ArrayOfint>
</AssignUserRole>
Can some one help me how can I pass this Array(Collection type Body parameter)?
Thanks.
The correct format would be this:
<AssignUserRole xmlns="http://tempuri.org/">
<roleIDs xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:int>7</a:int>
<a:int>8</a:int>
</roleIDs>
</AssignUserRole>
One easy way to find out what the expected format is for a certain operation is to use a WCF client with the same contract, send a message with it and look at the operation using Fiddler. The program below does that.
public class StackOverflow_6339286
{
[ServiceContract]
public interface ITest
{
[WebInvoke(Method = "PUT", UriTemplate = "users/role/{userID}", BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[OperationContract]
bool AssignUserRole(string userID, Collection<int> roleIDs);
}
public class Service : ITest
{
public bool AssignUserRole(string userID, Collection<int> roleIDs)
{
return true;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
WebChannelFactory<ITest> factory = new WebChannelFactory<ITest>(new Uri(baseAddress));
ITest proxy = factory.CreateChannel();
proxy.AssignUserRole("1234", new Collection<int> { 1, 2, 3, 4 });
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Also notice that there's a problem in your UriTemplate: the path variable {userId} cannot be of type int (it must be a string). This is fixed in the sample code above.
One more thing: if you don't want to use the default namespace for collections / arrays, you can use a [CollectionDataContract] class to change it. If instead of using Collection you used the class below, then the first body you tried should work:
[CollectionDataContract(Namespace = "http://tempuri.org/", ItemName = "roleID")]
public class MyCollection : Collection<int> { }

External HTTP endpoint in Azure worker role possible?

I am trying to host an external facing WCF service on Azure within a worker role.
I have a solution working very nice locally, but when I try to publish it to Azure it goes into an initializing/busy/stopped loop.
The information I've found around the internet says different things:
http://www.theworkflowelement.com/2011/01/worker-role-service-hosting-faq.html (impossible)
http://code.msdn.microsoft.com/WCF-Azure-Worker-Role-on-b394df49 (possible with hack)
Other sources say it's possible, but I don't have the rep to post more than two links.
The last one hangs on busy when I try to publish it.
Anyone know how to do this, or if it really is impossible? It would be very nice to host it in a worker role, so I don't have to use the svc and web.config mess that a web role entails.
This is the code I am using:
[ServiceContract(Namespace = "")]
public interface IMyService
{
[OperationContract]
[WebGet]
string Echo(string s);
}
public class MyService : IMyService
{
public string Echo(string s)
{
return "hey " + s;
}
}
public class TestPasswordValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
}
}
private static void StartService()
{
var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["HttpsEndpoint"];
var uri = new Uri(endpoint.Protocol + "://" + endpoint.IPEndpoint + "/myservice");
var host = new ServiceHost(typeof(MyService), uri);
host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new TestPasswordValidator();
var mexBehavior = new ServiceMetadataBehavior();
mexBehavior.HttpsGetEnabled = true;
mexBehavior.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(mexBehavior);
var soapBinding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
soapBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpsBinding(), "mex");
host.AddServiceEndpoint(typeof(IMyService), soapBinding, "Soap");
var restBinding = new WebHttpBinding(WebHttpSecurityMode.Transport);
restBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var restEndpoint = host.AddServiceEndpoint(typeof(IMyService), restBinding, "");
restEndpoint.Behaviors.Add(new WebHttpBehavior { HelpEnabled = true, DefaultOutgoingResponseFormat = WebMessageFormat.Json, AutomaticFormatSelectionEnabled = true, DefaultBodyStyle = WebMessageBodyStyle.WrappedRequest });
host.Open();
}
public override void Run()
{
StartService();
while (true)
{
Thread.Sleep(10000);
}
}
public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
return base.OnStart();
}
I figured out why this was happening. Worker roles need to run in elevated permissions to open HTTP ports. This setting however, is not available in the role settings gui. The setting the gui shows, which I thought controlled the permissions, is Full trust/Partial trust. I guess I have no idea what that does.
The correct setting is in the ServiceDefinition.csdef file, under WorkerRole.
<Runtime executionContext="elevated" />