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.
Related
Question: Is there a way how to quickly check whether particular pipename is being hosted in session 0 - preferabely during the ServiceHost.Open call?
Scenario:
Two processes PipeHost and PipeUser are trying to communicate on the system via pipe with name PeacePipe. They are not required to be started with special privileges.
PipeHost is started and hosts the PeacePipe without any problem.
PipeUser is started and connects to PeacePipe without any problem.
PipeUser tries to comunicate to PipeHost via PeacePipe, it sends messages but PipeHost doesn't see anything.
In fact PipeUser connected to DifferentPipeHostInSession0 that is hosting pipe with same name (but OS creates different pipe) in an elevated (or service) process.
Background:
ServiceHost.Open should throw AddressAlreadyInUseException when the selected pipename is already being hosted.
However it's not thrown if the pipe is hosted in session 0 and you are attempting to host the same pipe in different sessions. As windows named pipes are normally not te be used accross sessions. With the exception of pipe hosted in session 0. Any process can connect to such a pipe. This can lead to the above sceanrio.
Code:
[ServiceContract]
public interface IService
{
[OperationContract]
void Ping();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, IncludeExceptionDetailInFaults = true)]
public class Service: IService
{
public void Ping()
{
Console.WriteLine("Client Pinged me");
}
}
private static readonly Uri _pipeUri = new Uri("net.pipe://localhost/aaa");
private static readonly Binding _pipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
static void PipeHostTest()
{
ServiceHost serviceHost = new ServiceHost(new Service(), _pipeUri);
serviceHost.AddServiceEndpoint(typeof(IService), _pipeBinding, "");
try
{
//Fail here if same pipe already opened - even in Global space
serviceHost.Open();
Console.WriteLine("OPENED");
}
catch (AddressAlreadyInUseException e)
{
Console.WriteLine(e);
throw;
}
Console.ReadKey();
}
static void PipeClient()
{
ChannelFactory<IService> channelFactory =
new ChannelFactory<IService>(_pipeBinding, new EndpointAddress(_pipeUri));
var proxy = channelFactory.CreateChannel();
proxy.Ping();
}
static void Main(string[] args)
{
if (args.Any())
{
PipeClient();
}
else
{
PipeHostTest();
}
}
Run once without parameters elevated, once without parameters non-elevated. Both processes will host pipe with same name - but those are different pipes.
Then run once with any parameter. Client process will conect to the pipe hosted by elevated process.
Possible Solution:
Use a named mutex in global session new Mutex(true, "Global\\MutexForMyPipeName", out createdNew) to see if there is another process trying to do the same.
This however disqualifies even scenarios where the pipes are in 2 different sessions that do not colide.
Preferabely the ServiceHost.Open would take care about this for me as I'm using multiple bindings types (net.tcp, net.pipe, net.udp) and have single code for creating and hosting the ServiceHost. NamedPipes are the only ones that can allow creation of new host without AddressAlreadyInUseException exception while the address is actuall already in use.
I'm trying to self-host a singleton instance of a service and I'm obviously getting lost at a level of indirection...
I've got a base address of http://localhost:8050/. I'm not too bothered where the service endpoint is as long as it's predictable. For the moment, I'm trying to use /Manage/.
I'm able to browse to the base address and see a wsdl. If I scan through the wsdl, it points at /Manage/..
<wsdl:service name="EngineService">
<wsdl:port name="BasicHttpBinding_IEngineService" binding="tns:BasicHttpBinding_IEngineService">
<soap:address location="http://localhost:8050/Manage/"/>
</wsdl:port>
</wsdl:service>
When I consume the wsdl using the WcfTestClient, it lists all the correct methods, but calling any of them throw the following exception
System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at http://localhost:8050/Manage that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
Server stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at IEngineService.SupportedAgents()
at EngineServiceClient.SupportedAgents()
Inner Exception:
The remote server returned an error: (404) Not Found.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
Log messages show my instance methods never get called. The service doesn't enter a faulted state, it just looks like it's not there.
I'm listening as follows:
public static ServiceHost Listen<TServiceContract>(
TServiceContract instance,
int port,
string name
) {
//Added this for debugging, was previously just "name"
string endpoint = String.Format("http://localhost:{0}/{1}/", port, name);
var svcHost = new ServiceHost(
instance,
new Uri[] { new Uri(String.Format("http://localhost:{0}/", port)) });
/* Snip: Add a Faulted handler but it's never called */
ServiceEndpoint serviceHttpEndpoint = svcHost.AddServiceEndpoint(
typeof(TServiceContract),
new BasicHttpBinding {
HostNameComparisonMode = HostNameComparisonMode.WeakWildcard
}, endpoint); /*Using name instead of endpoint makes no difference beyond removing the trailing slash */
/* Snip: Add a ServiceDebugBehavior with IncludeExceptionDetailInFaults = true */
/* Snip: Add a ServiceMetadataBehavior with HttpGetEnabled = true */
try {
log.Trace("Opening endpoint");
svcHost.Open();
} catch () {
/* Lots of catches for different problems including Exception
* None of them get hit */
}
log.Info("Service contract {0} ready at {1}", typeof(TServiceContract).Name, svcHost.BaseAddresses.First());
return svcHost;
And calling the Listen() method as follows:
IEngineService wcfInstance = Resolver.Resolve<IEngineService>();
service = WcfHoster.Listen(wcfInstance, 8050, "Manage");
How can I track down what the problem is/debug further?
Additional info: The Service contract and minimal implementation:
[ServiceContract]
interface IEngineService {
[OperationContract]
List<string> Agents();
[OperationContract]
string Test();
[OperationContract]
List<string> SupportedAgents();
[OperationContract]
string Connect(string AgentStrongName, string Hostname);
}
And the implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class EngineService : IEngineService {
IAgentManager agentManager;
public EngineService(IAgentManager AgentManager) {
log.Debug("Engine webservice instantiating");
this.agentManager = AgentManager;
}
public string Connect(string AgentStrongName, string Hostname) {
log.Debug("Endpoint requested for [{0}], [{1}]", Hostname, AgentStrongName);
return agentManager.GetSession(AgentStrongName, Hostname);
}
public List<string> Agents() {
log.Debug("Current agents queried");
throw new NotImplementedException();
}
public List<string> SupportedAgents() {
log.Debug("Supported agents queried");
return agentManager.SupportedAgents().ToList();
}
public string Test() {
log.Warn("Test query");
return "Success!";
}
}
The test client can see the service and methods but throws the exception above when I click Invoke...
Edit: localhost resolves to IPv6 by default so I've tried using 127.0.0.1 explicitly at both ends. No difference.
I've tried taking the above code into a new project and get the same issue. Running the whole thing on someone else's machine didn't help either.
Service Trace viewer
Running a service trace on the server side, then examining the results in the viewer gives:
Failed to lookup a channel to receive an incoming message. Either the endpoint or the SOAP action was not found.
Config file: Since I need the executable to be able to make a decision about which Wcf service to present at runtime, I don't have any Wcf-related code in the config file.
This is probably a client / service binding mismatch. Please check the test client binding. You should also create a unit test by generating a proxy from the wsdl.
Ok. I have tried to reproduce your issue and I managed calling the host by removing "HostNameComparisonMode = HostNameComparisonMode.WeakWildcard" in order to get a default basichttp endpoint. Why do you need this?
Using the code from this answer - Async two-way communication with Windows Named Pipes (.Net) - I'm finding the maximum number of connections/clients at any one time is 10.
In the crude example below (this uses multiple threads - same thing happens if multiple processes are used) clients 1 to 10 will start and run as normal. However clients 11 and 12 will block when 'ProcessData' is called, eventually throwing a TimeoutException.
public static void Start()
{
// Start Server
new Thread(new ThreadStart(Server.MainRun)).Start();
// Start Clients
for (int i = 1; i <= 12; i++)
{
Thread.Sleep(500);
Client c = new Client(i.ToString());
new Thread(new ThreadStart(c.Run)).Start();
}
}
// Create a contract that can be used as a callback
public interface IMyCallbackService
{
[OperationContract(IsOneWay = true)]
void NotifyClient();
}
// Define your service contract and specify the callback contract
[ServiceContract(CallbackContract = typeof(IMyCallbackService))]
public interface ISimpleService
{
[OperationContract]
string ProcessData();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class SimpleService : ISimpleService
{
public string ProcessData()
{
// Get a handle to the call back channel
var callback = OperationContext.Current.GetCallbackChannel<IMyCallbackService>();
callback.NotifyClient();
return DateTime.Now.ToString();
}
}
class Server
{
public static void MainRun()
{
// Create a service host with an named pipe endpoint
using (var host = new ServiceHost(typeof(SimpleService), new Uri("net.pipe://localhost")))
{
host.AddServiceEndpoint(typeof(ISimpleService), new NetNamedPipeBinding(), "SimpleService");
host.Open();
Console.WriteLine("Simple Service Running...");
Console.ReadLine();
host.Close();
}
}
}
class Client : IMyCallbackService
{
string _id;
public Client(string ID)
{
_id = ID;
}
public void Run()
{
Console.WriteLine("Starting client : " + _id);
// Consume the service
var factory = new DuplexChannelFactory<ISimpleService>(new InstanceContext(this), new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/SimpleService"));
var proxy = factory.CreateChannel();
Console.WriteLine(proxy.ProcessData());
Console.WriteLine("Client finished : " + _id);
}
public void NotifyClient()
{
Console.WriteLine("Notification from Server");
}
}
If the client closes the channel when done (factory.Close()) then all clients will be able to run.
I understand this question - Number of Clients that can connect to a Named Pipe - is very similar but suggests there is no low limit.
This suggests the limit is 10 on Windows XP and 2000 machines - http://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream.aspx - except this is happening on a Windows 8 machine and Windows 2008 server.
Is there a way to change this limit? Am I missing something obvious?
Google brought me here a year after this question was asked. I figure I may as well post to help anyone else who ends up here. I think I know why the limit of 10.
Are you aware of the NetNamedPipeBinding.MaxConnections property?
Gets or sets the maximum number of connections, both inbound and outbound, that are allowed to endpoints configured with the named pipe binding. ... The maximum number of named pipe connections that are allowed with this binding. The default value is 10.
"guest" is correct and an old blog post from MSDN corroborates this as still being applicable in current .net releases.
It also suggests that the default setting was defined for use with development environments and "small" deployments.
From the other settings (eg, the buffer size) I'd suggest that >8kb per connection overhead would be expected.
I have not yet found any information on what issues may arise if the value is tuned for larger values (eg, >1000): the API appears tuned for shorter, burstier requests and I suspect for large values it may simply be inefficient (not so much for memory but just internal implementation) -
I'd welcome evidence either way on performance/issues (or success) with significant numbers of clients attaching to an endpoint.
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.
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