Sending a XML File through WCF Fails - c#

I've been trying to send a XML file from my WCF to my project without much luck. I have a Exception thrown from my program once the response is completed by WCF and sent to the Phone. I was hoping someone could please help me, as I have been looking around for an answer and found nothing. (The program uses XNA for a Windows Phone Applications)
[System.Net.WebException] {System.Net.WebException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound.
at System.Net.Browser.ClientHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
at System.Net.Browser.ClientHttpWebRequest.<>c__DisplayClasse.<EndGetResponse>b__d(Object sendState)
at System.Net.Browser.AsyncHelper.<>c__DisplayClass1.<BeginOnUI>b__0(Object sendState)
--- End of inner exception stack trace ---
at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
at System.Net.Browser.ClientHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)} System.Net.WebException
public string EndHighScoreList(System.IAsyncResult result) {
object[] _args = new object[0];
string _result = ((string)(base.EndInvoke("HighScoreList", _args, result)));
return _result;
}
IService.cs
[ServiceContract]
[XmlSerializerFormat]
public interface IService
{
[OperationContract]
void ParseScore(HighScore score);
[OperationContract]
string HighScoreList();
}
public class HighScore
{
[XmlElement]
public UInt32 m_rank;
[XmlAttribute]
public string m_name;
[XmlAttribute]
public UInt32 m_score;
}
Service.svc
public string HighScoreList()
{
XmlSerializer ser = new XmlSerializer(typeof(HighScore));
using (FileStream fs = new FileStream(HttpContext.Current.Server.MapPath("App_Data/Highscores.xml"), FileMode.OpenOrCreate))
{
return ser.Deserialize(fs).ToString();
}
}
Here's the requested code
void globalRecieve(object obj, DodgeService.HighScoreListCompletedEventArgs e)
{
try
{
string result = e.Result;
using (TextReader reader = new StringReader(result)){
XmlSerializer xml = new XmlSerializer(typeof(List<DodgeService.HighScore>));
foreach (DodgeService.HighScore sco in xml.Deserialize(reader) as List<DodgeService.HighScore>)
highScores.Add(sco);
}
}catch(Exception exception){
string error = exception.Message;
}
}
protected override void Initialize()
{
service = new DodgeService.ServiceClient("BasicHttpBinding_IService");
service.HighScoreListAsync(null);
service.HighScoreListCompleted += new EventHandler<DodgeService.HighScoreListCompletedEventArgs>(globalRecieve);
base.Initialize();
}

Personally I believe WCF sucks. The Configuration alone is a nightmare and if you change anything, you have to re-build your objects and any changes you've made you have to re-make.
You should migrate to ServiceStack. It handles everything for you. You only write the sending and receiving of business DTO objects. Sending and receiving files is basic stuff for it,
See this google search for several people asking similar questions but based on ServiceStack. Mythz is a project lead for ServiceStack and he answers their questions. It should get you started and you should see how EASY it is.
Just for future reference in case that google search doesn't give the same as I got, here is the search and first three responses;
"servicestack file upload"
Using ServiceStack to upload image files
How to use Servicestack PostFileWithRequest
ServiceStack client add attachment

The error says: "NotFound". It looks like the operation HighScoreList is not exposed / available at all. Try opening the path in your browser.

I kept having a Not Found error because, then the Windows Phone ran it was trying to connect to the service via LocalHost which wouldn't work as I needed it to connect to the development PC. The solution was to host the WCF service on a server and connect to the server or connect to the IP of the development PC.

Related

WCF Client not able to connect on some machines

I have a simple self host WCF Service. I have this service running on remote server. I am able to communicate to service with client running on my machine(I am local admin).
But when I run same client on different machine(non admin) they are not able to communicate.
I monitored resource manager and I see two random local ports are being open at each time of service call and call back. So I cannot open specific ports.
Any Idea what could be the possible reason or firewall configuration change on other machines?
I am very new to WCF. Please pardon me if its a basic question.
WCF Server Code
namespace CService
{
class Program
{
static void Main(string[] args) {
Console.Title = "C Service";
// Step 1 of the address configuration procedure: Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://" + GetServerIPPort.ServerIP + ":" + GetServerIPPort.Port + "/CService/Service");
// Step 2 of the hosting procedure: Create ServiceHost
ServiceHost selfHost = new ServiceHost(typeof(CSerice), baseAddress);
try
{
// Step 3 of the hosting procedure: Add a service endpoint.
selfHost.AddServiceEndpoint(typeof(ICService), new BasicHttpBinding(), "CService");
// Step 4 of the hosting procedure: Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5 of the hosting procedure: Start (and then stop) the service.
selfHost.Open();
Console.WriteLine("The Coemet Service is ready and its listening on {0}", baseAddress.AbsoluteUri.ToString() + ":" + baseAddress.Port.ToString());
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.ToString());
selfHost.Abort();
}
}
}
I have generatedProxy object with help of this link http://msdn.microsoft.com/en-us/library/ms733133(v=vs.110).aspx
My client code snippet looks like this.
string serviceEndPointAddress = "http://" + GetServerIPPort.ServerIP + ":" + GetServerIPPort.Port + "/CService/Service/CService";
var remoteAddress = new System.ServiceModel.EndpointAddress(new Uri(serviceEndPointAddress));
object rawOutput;
using (var client = new CServiceClient(new BasicHttpBinding(), remoteAddress))
{
client.Endpoint.Binding.SendTimeout = new TimeSpan(0, 0, 0, 100);
try
{
rawOutput = client.GetData(Identifier, field, date);
}
catch (Exception e)
{
errorMsg = e.ToString();
}
}\n
Error trowed at "client.GetData(Identifier, field, date)"
System.Runtime.Serialization.InvalidDataContractException: Type 'System.Threading.Tasks.Task1[System.Object]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type)
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
at System.Runtime.Serialization.XsdDataContractExporter.GetSchemaTypeName(Type type)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.CreatePartInfo(MessagePartDescription part, OperationFormatStyle style, DataContractSerializerOperationBehavior serializerFactory)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.CreateMessageInfo(DataContractFormatAttribute dataContractFormatAttribute, MessageDescription messageDescription, DataContractSerializerOperationBehavior serializerFactory)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter..ctor(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory)
at System.ServiceModel.Description.DataContractSerializerOperationBehavior.GetFormatter(OperationDescription operation, Boolean& formatRequest, Boolean& formatReply, Boolean isProxy)
at System.ServiceModel.Description.DataContractSerializerOperationBehavior.System.ServiceModel.Description.IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
at System.ServiceModel.Description.DispatcherBuilder.BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch)
at System.ServiceModel.Description.DispatcherBuilder.BuildProxyBehavior(ServiceEndpoint serviceEndpoint, BindingParameterCollection& parameters)
at System.ServiceModel.Channels.ServiceChannelFactory.BuildChannelFactory(ServiceEndpoint serviceEndpoint, Boolean useActiveAutoClose)
at System.ServiceModel.ChannelFactory.CreateFactory()
at System.ServiceModel.ChannelFactory.OnOpening()
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.ChannelFactory.EnsureOpened()
at System.ServiceModel.ChannelFactory1.CreateChannel(EndpointAddress address, Uri via)
at System.ServiceModel.ClientBase1.CreateChannel()
at System.ServiceModel.ClientBase1.CreateChannelInternal()
at System.ServiceModel.ClientBase`1.get_Channel()
at CServiceClient.GetData(String Identifier, String field, DateTime date)
In .NET 4.5, there is new support for task-based asynchronous operations in WCF. When you generate a proxy on your development machine using VS 2012 or later - it can include these by default.
Now the new machine that you are using is likely running on .NET 4.0 and as a result does not know what the heck to do with the task-based asynchronous operation - hence the exception.
It's a pretty simple fix, to support clients running .NET 4.0 you just need to do either of the following in Service Reference Settings:
Uncheck Allow generation of asynchronous operations
Select Generate asynchronous operations instead of Generate task-based operations
Special thanks goes to this blog post.
According to the exception you are passing an object that is not serializable. I suspect this is the Identifier in the following line of code:
rawOutput = client.GetData(Identifier, field, date);
According to the stack trace the CService is expecting two strings and a datetime:
System.ServiceModel.ClientBase`1.get_Channel() at
CServiceClient.GetData(String Identifier, String field, DateTime date)
If you need to pass a custom object (data transfer object) using WCF you should use the DataContractAttribute attribute for the class and the DataMemberAttribute for each member, like this:
[DataContract]
public class Identifier
{
[DataMember]
public string Id {get;set;}
}
I too was facing the same issue, when I created a WCF Service on framework 4.5 higher and was trying to deploy on the IIS 8(i.e App pool 4.0), the above steps didn't solved my issue, So I recreated my full solution from scratch in framework 4.0 and re-deployed it. While consuming the services, followed the above steps to uncheck "Allow generation of asynchronous operations" Resolved my problem.

.NET remoting service seemingly crashes, and stops responding to clients

I have a .NET Remoting service which works fine most of the time. If an exception or error happens, it logs the error to a file but still continues to run.
However, about once every two weeks the service stops responding to clients, which causes the client appication to crash with a SocketException with the following message:
A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
No exception or stack trace is written to our log file, so I can't figure out where the service is crashing at, which leads me to believe that it is somewhere outside of my code which is failing. What additional steps can I take to figure out the root cause of this crash? I would imagine that it writes something to an EventLog somewhere, but I am not super familiar with Windows' Event Logging system so I'm not exactly sure where to look.
Thanks in advance for any assistance with this.
EDIT: Forgot to mention, stopping or restarting the service does nothing, the service never responds. I need to manually kill the process before I can start the service again.
EDIT 2:
public class ClientInfoServerSinkProvider :
IServerChannelSinkProvider
{
private IServerChannelSinkProvider _nextProvider = null;
public ClientInfoServerSinkProvider()
{
}
public ClientInfoServerSinkProvider(
IDictionary properties,
ICollection providerData)
{
}
public IServerChannelSinkProvider Next
{
get { return _nextProvider; }
set { _nextProvider = value; }
}
public IServerChannelSink CreateSink(IChannelReceiver channel)
{
IServerChannelSink nextSink = null;
if (_nextProvider != null)
{
nextSink = _nextProvider.CreateSink(channel);
}
return new ClientIPServerSink(nextSink);
}
public void GetChannelData(IChannelDataStore channelData)
{
}
}
public class ClientIPServerSink :
BaseChannelObjectWithProperties,
IServerChannelSink,
IChannelSinkBase
{
private IServerChannelSink _nextSink;
public ClientIPServerSink(IServerChannelSink next)
{
_nextSink = next;
}
public IServerChannelSink NextChannelSink
{
get { return _nextSink; }
set { _nextSink = value; }
}
public void AsyncProcessResponse(
IServerResponseChannelSinkStack sinkStack,
Object state,
IMessage message,
ITransportHeaders headers,
Stream stream)
{
IPAddress ip = headers[CommonTransportKeys.IPAddress] as IPAddress;
CallContext.SetData("ClientIPAddress", ip);
sinkStack.AsyncProcessResponse(message, headers, stream);
}
public Stream GetResponseStream(
IServerResponseChannelSinkStack sinkStack,
Object state,
IMessage message,
ITransportHeaders headers)
{
return null;
}
public ServerProcessing ProcessMessage(
IServerChannelSinkStack sinkStack,
IMessage requestMsg,
ITransportHeaders requestHeaders,
Stream requestStream,
out IMessage responseMsg,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{
if (_nextSink != null)
{
IPAddress ip =
requestHeaders[CommonTransportKeys.IPAddress] as IPAddress;
CallContext.SetData("ClientIPAddress", ip);
ServerProcessing spres = _nextSink.ProcessMessage(
sinkStack,
requestMsg,
requestHeaders,
requestStream,
out responseMsg,
out responseHeaders,
out responseStream);
return spres;
}
else
{
responseMsg = null;
responseHeaders = null;
responseStream = null;
return new ServerProcessing();
}
}
This is like trying to find out why nobody picks up the phone when you call a friend. And the problem is that his house burned down to the ground. An imperfect view of what is going on is the core issue, especially bad with a service because there is so little to look at.
This can't get better until you use that telephone to talk to the service programmer and get him involved with the problem. Somebody is going to have to debug this. And yes, it will be difficult, failing once every two weeks might not be considered critical enough. Or too long to sit around waiting for it to happen. Only practical thing you can do to help is create a minidump of the process and pass that to the service programmer so he's got something to poke at. If the service runs on another machine then get the LAN admin involved as well.
The issue was due to a deadlock caused in my code, if memory serves I had two locking objects and I locked one from inside the other, essentially making them wait for each other. I was able to determine this by hooking up a debugger to the remote service.

Saving image to database as varbinary (part 3)

This is yet again a problem I have with saving images in silverlight to my database, I thought I had it all working untill I tried it out with a different image...
I save images to my database with following method.
I first convert the image to an array of byte and then send it to my service.
private void btnUpload_Click(object sender, RoutedEventArgs e)
{
//nieuwe instantie van de klasse "Afbeelding", om later door te sturen naar service
Afbeelding a = new Afbeelding();
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "JPEG files|*.jpg";
if (openFileDialog.ShowDialog() == true)
{
//Afbeelding ophalen via open dialoog
Stream stream = (Stream)openFileDialog.File.OpenRead();
string fileName = openFileDialog.File.Name;
//Converteren naar bytes
//byte[] bytes = BinaryConverter.convertToByte(stream);
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
//aan de instantie de Binary waarde van de afbeelding meegeven om naar de database te sturen
a.id = 1;
a.source = new Binary { Bytes = bytes };
}
EditAfbeeldingServiceClient client = new EditAfbeeldingServiceClient();
client.UpdateAfbeeldingCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_UpdateAfbeeldingCompleted);
client.UpdateAfbeeldingAsync(a);
}
And in my service I do this:
[OperationContract]
public void UpdateAfbeelding(Afbeelding a)
{
var query = (from p in dc.Afbeeldings
where p.id == a.id
select p).SingleOrDefault();
query.source = a.source;
dc.SubmitChanges();
}
Now during my testing this all worked, but I only used one image to test...
So when I tried just now with a different image, I get the following error:
System.ServiceModel.ProtocolException: The remote server returned an unexpected response: (400) Bad Request. In Silverlight, a 404 response code may be reported even when the service sends a different error code. ---> System.Net.WebException: The remote server returned an error: NotFound. ---> System.Net.WebException: The remote server returned an error: NotFound.
at System.Net.Browser.ClientHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
at System.Net.Browser.ClientHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4(Object sendState)
at System.Net.Browser.AsyncHelper.<>c__DisplayClass4.<BeginOnUI>b__0(Object sendState)
--- End of inner exception stack trace ---
at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
at System.Net.Browser.ClientHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)
--- End of inner exception stack trace ---
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result)
at OndernemersAward.EditAfbeeldingServiceReference.EditAfbeeldingServiceClient.EditAfbeeldingServiceClientChannel.EndUpdateAfbeelding(IAsyncResult result)
at OndernemersAward.EditAfbeeldingServiceReference.EditAfbeeldingServiceClient.OndernemersAward.EditAfbeeldingServiceReference.EditAfbeeldingService.EndUpdateAfbeelding(IAsyncResult result)
at OndernemersAward.EditAfbeeldingServiceReference.EditAfbeeldingServiceClient.OnEndUpdateAfbeelding(IAsyncResult result)
at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)
I can't really read anything out of that error so once again, I'm stuck here.
I apologise for using these boards so much, but I really wouldn't if it wasn't needed so much.
I have set the maximum to send through to a high number, but it still doesn't work.
<readerQuotas maxDepth="32"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647" />
You can find my web.config here: http://pastebin.com/whMs5h1w
Thank you for your help, I really appreciate it.
Thomas
Edit: I managed to get a more readable error with enabling tracing, hope this helps anyone :)
WCF has various limits built in. One is the maxReceivedMessageSize which is 65536 bytes by default and another one is maxArrayLength (not sure what the default is). There is a good chance you have exceeded one of the two (or both). You can change those in your service configuration. This article on MSDN contains some example configurations.
Also enabling tracing for your service might provide you with some more insight of which limits are hit.
Btw: There is a File.ReadAllBytes method.
Edit: Apparently there is a tool called Fiddler which can help tracking these issues down.

Custom TraceListener Windows application

So far in my application i am logging all the trace events to a file on my hard drive.
I now want to enhance it in the form of a separate windows trace application which listens
to the trace from the Main application (as they are generated) and report it on a
gridview like interface. Now the question is :
What kind of TraceListener i have to use to get a maximum benefit interms of speed at which the log information is read ?
Restrictions
Owing to certain restrictions, i cannot use the database logging and reading approach
Will listening to the application eventLogs help in any way?
Thanks for the suggestions and time.
The default trace listener will use the win32 api OutputDebugString. You can listen to messages passed to this method using existing tools. Have a look at this, for instance:
http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
Maybe that will save you the time it takes to write your own.
If you do want to write your own and your main concern is to get trace messages as fast as possible to the trace viewer application, then you could have your TraceListener accept network connections from the viewer. Whenever a trace messages is handled by the trace listener, you would write to the network. If you're not concerned about being able to view trace messages on a remote machine, then listening to whats put to OutputDebugString is also an option, of course.
This would effect the performance of the application doing the tracing of course so writing to the network is best done asynchronously without blocking the trace write call. While writing to the network you would have to add trace messages to a queue to process.
Here is a simple example which probably works:
public class NetworkPublishingTraceListener : TraceListener {
private List<string> messages = new List<string>();
private AutoResetEvent messagesAvailable = new AutoResetEvent(false);
private List<TcpClient> traceViewerApps;
private object messageQueueLock = new object();
public NetworkPublishingTraceListener(int port) {
// Setup code for accepting and dealing with network connections.
(new Thread(BackgroundThread) { IsBackground = true }).Start();
}
public override void Write(string message) {
if (traceViewerApps.Count == 0) {
return;
}
lock (messageQueueLock) {
messages.Add(message);
}
messagesAvailable.Set();
}
public override void WriteLine(string message) {
Write(message + Environment.NewLine);
}
private void BackgroundThread() {
while (true) {
messagesAvailable.WaitOne();
List<string> messagesToWrite;
lock (messageQueueLock) {
messagesToWrite = messages;
messages = new List<string>();
}
traceViewerApps.ForEach(viewerApp => {
StreamWriter writer = new StreamWriter(viewerApp.GetStream());
messagesToWrite.ForEach(message => writer.Write(message));
});
}
}
}
Why don't you use log4net or nlog ?

How can I expose a .Net 2.0 Webservice to a Silverlight client?

I have a trivial .Net 2.0 SOAP web service. I want to access it from Silverlight application that is hosted on the same server, but different port. I know that for this to work I need to provide a clientaccesspolicy.xml or crossdomain.xml policy file (the service is available at http://example:8085/RemoteObject.rem?wsdl , so the policy file has to be present at http://example:8085/crossdomain.xml). What should I add to the following simple web service to self-serve the policy file like the WCF example does?
The web service is being run on Mono, although that shouldn't change anything - there's just no IIS involved.
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
namespace ConsoleApplication1
{
class RemoteObject : MarshalByRefObject
{
static void Main()
{
var channel = new HttpChannel(8085);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemoteObject), "RemoteObject.rem",
WellKnownObjectMode.Singleton);
Console.WriteLine("Press ENTER to exit the server.");
Console.ReadLine();
}
public DateTime Now()
{
return DateTime.Now;
}
}
}
EDIT: because of all the unusable answers, let me repeat myself: I need to do this with .Net 2.0, not 3.0 or 3.5. WCF is not available.
I dont know much about the deploymnets in MONO. I would suggest a different approach if you didnt find any better approaches for your question.
Instead of directly calling the webservice from silverlight app.. you can invoke a javascript method from your managed silverlight code using
string sJson = HtmlPage.Window.Invoke("JSMethod", new string[] { strInParam }) as string;
and fire a AJAX request (from JS method) to your server and will internally make a call to the webservice deployed in MONO (from server) and return a JSON formatted result.
I have implemented this approach in my projects and its working fine..
just an alternative..
Ok, I can say that answer is not an easy step. Please look at Cassini open source web server, and you will have to implement a small web server in your application and run your custom service in your custom web server.
And the best way to open this silverlight would be, create an IFrame and let it load the html/aspx from your custom web server from your custom port itself. So you would not need any cross domain policy problems.
EDIT2: my coworker has found a usable solution: Web Service Enhancements from Microsoft. It does need IIS and it has been deprecated with the introduction of WCF, but works well with plain .Net Framework 2.0 and should be deployable with Mono XSP.
EDIT: solution below is pointless, because .Net 2.0 exposes web services using SOAP 1.1 rpc/encoded model, and Silverlight requires SOAP 1.2 document/literal. So while the workaround works for the problem indicated in the question, the web service still cannot be consumed.
I managed to make this work without resorting to extreme hacks. The key to my solution was to insert an additional IServerChannelSink into the request processing queue. So, I changed
var channel = new HttpChannel(8085);
to register my custom IServerChannelSink before the normal pipeline:
var provider = ChainProviders(
new PolicyServerSinkProvider(),
new SdlChannelSinkProvider(),
new SoapServerFormatterSinkProvider(),
new BinaryServerFormatterSinkProvider());
var channel = new HttpChannel(new Hashtable(1) {{"port", 8085}}, null, provider);
I use a helper method to chain the sink providers together:
private static IServerChannelSinkProvider ChainProviders(
params IServerChannelSinkProvider[] providers)
{
for (int i = 1; i < providers.Length; i++)
providers[i-1].Next = providers[i];
return providers[0];
}
PolicyServerSinkProvider simply creates a PolicyServerSink:
internal class PolicyServerSinkProvider : IServerChannelSinkProvider
{
public void GetChannelData(IChannelDataStore channelData){}
public IServerChannelSink CreateSink(IChannelReceiver channel)
{
IServerChannelSink nextSink = null;
if (Next != null)
nextSink = Next.CreateSink(channel);
return new PolicyServerSink(channel, nextSink);
}
public IServerChannelSinkProvider Next { get; set; }
}
PolicyServerSink delegates all messages down the chain, except when it gets a request for crossdomain.xml - then it writes the needed xml into the response stream.
internal class PolicyServerSink : IServerChannelSink
{
public PolicyServerSink(
IChannelReceiver receiver, IServerChannelSink nextSink)
{
NextChannelSink = nextSink;
}
public IDictionary Properties { get; private set; }
public ServerProcessing ProcessMessage(
IServerChannelSinkStack sinkStack, IMessage requestMsg,
ITransportHeaders requestHeaders, Stream requestStream,
out IMessage responseMsg, out ITransportHeaders responseHeaders,
out Stream responseStream)
{
if (requestMsg != null || ! ShouldIntercept(requestHeaders))
return NextChannelSink.ProcessMessage(
sinkStack, requestMsg, requestHeaders, requestStream,
out responseMsg, out responseHeaders, out responseStream);
responseHeaders = new TransportHeaders();
responseHeaders["Content-Type"] = "text/xml";
responseStream = new MemoryStream(Encoding.UTF8.GetBytes(
#"<?xml version=""1.0""?><!DOCTYPE cross-domain-policy SYSTEM "
+ #"""http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"">"
+ #"<cross-domain-policy><allow-access-from domain=""*"" />"
+ #"</cross-domain-policy>")) {Position = 0};
responseMsg = null;
return ServerProcessing.Complete;
}
private static bool ShouldIntercept(ITransportHeaders headers)
{
return ((string) headers["__RequestUri"]).Equals(
"/crossdomain.xml", StringComparison.InvariantCultureIgnoreCase);
}
public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack,
object state, IMessage msg, ITransportHeaders headers, Stream stream)
{
}
public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack,
object state, IMessage msg, ITransportHeaders headers)
{
throw new NotSupportedException();
}
public IServerChannelSink NextChannelSink { get; private set; }
}
This can also be used to serve other files together with the web service. I am currently using this method to host my Silverlight application (the consumer of the web service) without a separate http server.
Take a look at this http://blogs.msdn.com/carlosfigueira/archive/2008/03/07/enabling-cross-domain-calls-for-silverlight-apps-on-self-hosted-web-services.aspx

Categories

Resources