Print Page | Close Window

Silverlight Net.TCP Remotable TracePublish Support?

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2010
Forum Discription: For .NET 4.0
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=2387
Printed Date: 12-Apr-2026 at 4:08pm


Topic: Silverlight Net.TCP Remotable TracePublish Support?
Posted By: JoeGershgorin
Subject: Silverlight Net.TCP Remotable TracePublish Support?
Date Posted: 22-Dec-2010 at 1:04pm
Silverlight 4 added the ability to consume Net.TCP WCF services, which is the way the IdeaBlade Remoteable TracePublisher is exposed. So I went about the task to see if I could consume it in a Silverlight 4 app.

I made the publisher remoteable in Global.asax:

// ------------------------------------------------------------
// Silverlight Client Notes:    
// ------------------------------------------------------------
// Only ports 4502-4534 are allowed for Silverlight access. Also 
// a clientaccesspolicy.xml which allows the connection port 
// must exist in the root domain on port 80 
// (eg. http://localhost/clientaccesspolicy.xml)        
//
// ------------------------------------------------------------
// Sample clientacesspolicy.xml file
// ------------------------------------------------------------
// <?xml version="1.0" encodinga="utf-8"?>
// <access-policy>
//  <cross-domain-access>
//    <policy>
//      <allow-from>
//        <domain uri="*" />
//      </allow-from>
//      <grant-to>
//        <socket-resource port="4502-4534" protocol="tcp" />
//      </grant-to>
//    </policy>
//  </cross-domain-access>
// </access-policy>
//
// The port restriction and clientaccesspolicy.xml required for Silverlight is not
// needed for Silverlight trusted (Out of Browser) applications.
    
IdeaBlade.Core.TracePublisher.LocalInstance.MakeRemotable(4502, "TracePublisher");
    
The next hurdle was that some of the Silverlight types that need to be passed were not decorated with DataContracts for their Silverlight versions. So I created proxy classes that defiend the contract that are shared between the client and server:

[ServiceContract(Name = "ITracePublisher", CallbackContract = typeof(ITraceSubscriberCallbackEx))]
public interface ITracePublisherEx
{
    // Methods
    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginPing(AsyncCallback cb, object state);      
    bool EndPing(IAsyncResult r);

    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginSubscribe(Guid pKey, ITraceSubscriberCallback pSubscriber, AsyncCallback cb, object state);
    void EndSubscribe(IAsyncResult r);

    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginUnsubscribe(Guid pKey, AsyncCallback cb, object state);
    void EndUnsubscribe(IAsyncResult r);       

[ServiceContract(Name = "ITraceSubscriberCallback")]
public interface ITraceSubscriberCallbackEx
{
    [OperationContract(IsOneWay = true)]
    void OnPublish(TraceMessage pTraceMessage);
}

[KnownType(typeof(CustomTraceSubscriber))]    
[DataContract]
public class CustomTraceSubscriber : ITraceSubscriberCallback, IKnownType, ITraceSubscriberCallbackEx
{
    public Guid Id { get; private set; }
    private ITracePublisherEx _serverTracePublisher;

    public CustomTraceSubscriber()
    {
        Id = Guid.NewGuid();
    }

    public CustomTraceSubscriber Connect(string hostname = "localhost", string port = "4502", string serviceName = "TracePublisher")
    {
        if (_serverTracePublisher == null)
        {
            var customBinding = new CustomBinding(new TcpTransportBindingElement());
            var endpointAddress = new EndpointAddress("net.tcp://" + hostname + ":" + port + "/" + serviceName);
            var callbackInstance = new InstanceContext(this);

            var factory = new DuplexChannelFactory<ITracePublisherEx>(callbackInstance, customBinding, endpointAddress);
            _serverTracePublisher = factory.CreateChannel();    
        }

        return this;
    }        

    public void SubscribeAsync(Action<IAsyncResult> action)
    {
        _serverTracePublisher.BeginSubscribe(Id, this, args =>
            {
                _serverTracePublisher.EndSubscribe(args);            
                   
                if (action != null)
                {
                    action(args);
                }
            }, null);
    }

    public void UnsubscribeAsync(Action<IAsyncResult> action)
    {
        _serverTracePublisher.BeginUnsubscribe(Id, args =>
        {
            _serverTracePublisher.EndUnsubscribe(args);

            if (action != null)
            {
                action(args);
            }
        }, null);
    }

    public void PingAsync(Action<bool> action)
    {
        _serverTracePublisher.BeginPing(
            args =>
            {
                var result = _serverTracePublisher.EndPing(args);

                if (action != null)
                {
                    action(result);
                }                        
            },
        null);
    }

    [OperationContract(IsOneWay = true)]
    public void OnPublish(TraceMessage pTraceMessage)
    {            
        System.Diagnostics.Debug.WriteLine(pTraceMessage.Message);
    }
}

Then on the client:

var traceSubscriber = new CustomTraceSubscriber().Connect();         

// PingAsync Works fine
traceSubscriber.PingAsync(
result => Execute.OnUIThread(() => MessageBox.Show(result.ToString())));


// SubscribeAsync Throws following exception:
-------------------------------------------
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:pKey. The InnerException message was 'XML 'Element' 'http://tempuri.org/:pKey' does not contain expected attribute 'http://schemas.microsoft.com/2003/10/Serialization/:Type'. The deserializer has no knowledge of which type to deserialize. Check that the type being serialized has the same contract as the type being deserialized.'.  Please see InnerException for more details.
-------------------------------------------

traceSubscriber.SubscribeAsync(args=>
{
var result = args;
  Execute.OnUIThread(() => MessageBox.Show(result.IsCompleted.ToString()));
});
            

I think the reason the call to Subscribe fails is becasue on the server the Subscribe operation is decorated with a NetDataContractFormat attribute which applies NetDataContractSerializerOperationBehavior which applies as NetDataContractSerializer.

NetDataContractSerializer is not supported in Silverlight (obviously DataContractSerializer is supported). Is there a reason why NetDataContractSerializer is used?

From what I read the use of it is discouraged (http://www.pluralsight-training.net/community/blogs/aaron/archive/2006/04/21/22284.aspx) if possible, which is why you have to create your own custom attribute to even apply it.

Would it be possible in the future to modify your contracts to make them Silverlight compatible? Thanks for any feedback you can provide.



Replies:
Posted By: kimj
Date Posted: 22-Dec-2010 at 2:59pm
In a Silverlight application tracing actually occurs in two locations:  the Silverlight application generates trace messages for local activities on that client instance, and the BOS generates trace messages for all server-side activities.  So the first question is do you truly want to subscribe to the BOS tracing or just see local trace messages (sorry, we don't have a central logging  facility). 
 
If you do want to see the BOS tracing, as you found the trace publisher is not consumable by a Silverlight application, but we'll look into changing this.  For the record, I'll mention that the two trace viewers which ship with DevForce (which come in WPF and WinForms flavors) do by default subscribe to the BOS publisher.
 
For local Silverlight trace messages, we do provide a simple subscriber in the sample TraceViewer user control, which you can find buried somewhere in the Deployment folder of the DevForce Resource Center samples download.   We also provide the ITraceLogger interface, which defines a very simple interface to receive local trace messages on any platform.  You can find more information on this in the DRC.
 
 


Posted By: JoeGershgorin
Date Posted: 22-Dec-2010 at 5:12pm
Hi Kim,

Thanks for the quick reply. Yes, I'm aware of the included client and server trace viewers. I've implemented my own versions of the client trace viewer based on Telerik data grids. One that runs inside a popup RadWindow that can be called by a keyboard hotkey. It has find as you type keyword search capability, detail views, auto scrolling, and the ability to export the log to excel, csv, etc... I have another implementation of the Silverlight client trace viewer that receives local trace messages via the local messaging api and can run in a different physical browser window (or out of browser) parallel to another local Silverlight application it is monitoring. 

I'm looking to replicate/extend the functionality of the Winform/WPF BOS trace viewers included with DevForce in Silverlight form that can bundled with projects I'm working on. Right now the furthest I've gotten is a server BOS .log file downloader/viewer. But I would like live BOS trace viewing the way the Win/WPF apps work but through a Silverlight OOB app. I mean it's 99% of the way there with the only hurdle being the NetDataContractSerializerlization of the Subscribe operation. I guess as a work around I could probably create my own push service server side. But I was hoping to be able to create an SL OOB BOS Trace viewer that didn't require app modification beyond turning on the remote trace publisher. 

Put me in for a +1 vote for SL compatibility, thanks.


Posted By: kimj
Date Posted: 22-Dec-2010 at 6:22pm
OK, we've added this to the feature request queue. 



Print Page | Close Window