Friday, October 30, 2009

Logging proxy

I learnt today that I can create a proxy that intercepts calls to an object, do anything with the call and forward the call to the appropriate object. The same is true for the results. The whole thing is implemented by using a RealProxy implementation. I will demonstrate the functionality with .NET Remoting.

The common interface

namespace Common
{
public interface IServer
{
int Add(int a, int b);
}
}

The server implementation

using Common;

namespace Server
{
class ServerImpl: MarshalByRefObject, IServer
{
#region IServer Members

public int Add(int a, int b)
{
return a + b;
}

#endregion

public override object InitializeLifetimeService()
{
return null;
}
}
}

The server loop

static void Main()
{
// Windows Forms settings
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

// Remoting loop
RemotingServices.Marshal(new SzerverImplementacio(), "Server");
ChannelServices.RegisterChannel(new TcpChannel(8080), false);

// Windows Froms Window
Application.Run(new Form1());
}

The logging proxy

using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels;
using System.Collections;
 
namespace Kliens
{
    public class LogProxy : RealProxy
    {
        String uri;
        MarshalByRefObject obj;
        IMessageSink _sinkChain;
 
        public LogProxy(Type type, string uri) : base(type)
        {
            this.uri = uri;
 
            IChannel[] registeredChannels = ChannelServices.RegisteredChannels;
            foreach (IChannel chnl in registeredChannels)
            {
                if (chnl is IChannelSender)
                {
                    IChannelSender chnlSender = (IChannelSender)chnl;
                    _sinkChain = chnlSender.CreateMessageSink(uri, null, out uri);
                    if (_sinkChain != null)
                        break;
                }
            } 
 
            if (_sinkChain == null)
            {
                throw new Exception("No channel has been found for " + uri);
            }
 
        }
 
        public override IMessage Invoke(IMessage msg)
        {
            Console.WriteLine("LogProxy.Invoke Start");
            Console.WriteLine("");
 
            // Set message URI
            msg.Properties["__Uri"] = uri;
 
            // List call properties
            Console.WriteLine("Call properties:");
            foreach (var key in msg.Properties.Keys)
            {
                Console.WriteLine("{0}: {1}", key, msg.Properties[key]);
                if (key.ToString() == "__Args")
                {
                    Console.WriteLine("Arguments:");
 
                    object[] args = (object[])msg.Properties[key];
                    foreach (var arg in args)
                    {
                        Console.WriteLine("arg: {0}", arg);
                    }
                }
            }
 
            // Process the request mesage
            IMessage retMsg = _sinkChain.SyncProcessMessage(msg);
 
            // List return properties
            Console.WriteLine("Return properties:");
            foreach (var key in retMsg.Properties.Keys)
            {
                Console.WriteLine("{0}: {1}", key, retMsg.Properties[key]);
            }
 
            // Process the return message
            return retMsg;
 
        }
    }
}

Call the server from the client

// Without logging proxy:
// IServer server = (IServer)Activator.GetObject(typeof(IServer), "tcp://localhost:8080/Server");
 
TcpChannel chnl = new TcpChannel();
ChannelServices.RegisterChannel(chnl, false);
 
LogProxy prx = new LogProxy(typeof(IServer), "Tcp://localhost:8080/Server");
IServer server = (IServer)prx.GetTransparentProxy();
MessageBox.Show(server.Add(2, 3).ToString());

Result

All the call end response properties will be listed in the Output window (or in the Console). The call will happen as there were no proxy class taking part in the call.

Apart Remoting this is a good solution for generating any kind of proxies:

  • Virtual proxies
  • Security proxies

RealProxy is a good extension point for an application.

Thursday, October 29, 2009

Hiding XAML designer by default

I hate Visual Studio 2008 XAML Designer. It’s slow and can be easily killed (I mean XAML Designer can’t be displayed, just an error message instead). I prefer Expression Blend 3 for GUI design.

So here are the steps to hide the designer and go straight to the text editor:

  1. Open Tools/Options
  2. Select Text Editor/XAML/Miscellaneous
  3. Under ‘Default View’ select ‘Always open documents in full XAML view’

After setting this option, you will still be able to change to designer using a tab, but the editor will be the default view for XAML files. Faster loading an (hopefully) less crashes.

XAML Combobox SelectedItem binding

Combobox SelectedItem binding in XAML  did not work for me at first.

This is the XAML for binding:

<ComboBox x:Name="format" DisplayMemberPath="Text" SelectedItem="{Binding Format}" IsEditable="False"   />

This is the code behind:

PublishingFormatRepository pf = new PublishingFormatRepository(Current.BookshelfPath);
List<PublishingFormat> publishingFormats = pf.All as List<PublishingFormat>;
format.ItemsSource = publishingFormats;
if (publishingFormats != null) format.SelectedItem = publishingFormats[0];

set
{
_book = value;
DataContext = _book;
}

It’s simple, it should work. I attached a binding tracer to the binding extension but there was no problem. After a while something came on my mind: object equality. I checked something like this:

_book.Format == publishingFormats[0]

It should have been true but it was false.

So the solution is simple:

[Serializable]
public class PublishingFormat : IEquatable<PublishingFormat>
{
////////////////
// Members here
////////////////

public override bool Equals(object obj)
{
return Equals(obj as PublishingFormat);
}

#region IEquatable<PublishingFormat> Members

public virtual bool Equals(PublishingFormat other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Id.Equals(Id);
}

#endregion

public override int GetHashCode()
{
return Id.GetHashCode();
}

public static bool operator ==(PublishingFormat left, PublishingFormat right)
{
return Equals(left, right);
}

public static bool operator !=(PublishingFormat left, PublishingFormat right)
{
return !Equals(left, right);
}
}

The class implements the IEquatable interface and overrides the Equals function and the ==, != operators. In my case the equality will be evaluated by comparing the Id fields of the classes.

Now the simple XAML expression above works.

Link: How to: Define Value Equality for a Type (C# Programming Guide) [MSDN]