views:

35

answers:

2

I've got a library which has two input formats for the object model described by the library. I currently use an event subscription model for raising errors/warnings/verbose messages to the end user of the library. This hasn't proven to be the cleanest model, and I was wondering if there was a relevant design pattern or something similar found in the .Net Framework (when in Rome) to better handle this situation.

// Rough outline of the current code
public abstract class ModelReader : IDisposable
{
    public abstract Model Read();

    public event EventHandler<MessageAvailableEventArgs> MessageAvailable;

    protected virtual void RaiseError(string message)
    {
        var handler = this.MessageAvailable;
        if (handler != null)
        {
            handler(this, new MessageAvailaibleEventArgs(
                TraceEventType.Error, message);
        }
    }
}

Edit: some clarification. The Read routine will already fail fast on all Fatal errors using an exception. The messages are being logged to potentially multiple sources on the user's end, thus any pattern should avoid limiting the number of potential sources.

A: 

If there are error that stop the library from doing it's work I would use exception.

If this is strictly for tracing and instrumentation I would either go with your pattern or a TextWriter pattern, where the consumer would pass in a text writer and you would write your trace info to that, the only problem with that is you can only have one external subscriber. but it results in a slightly cleaner code.

 public TextWriter Log { get; set; }

 private void WriteToLog(string Message)
 {
    if (Log != null) Log.WriteLine(message);
 }
Keivan
I will clarify a bit regarding exception handling and logging.
sixlettervariables
+1  A: 

I think event subscription mode is OK. But you can consider interface instead. This might give you more flexibility. Something like this:

public interface IMessageHandler
{
    void HandleMessage(object sender, MessageAvailaibleEventArgs eventArgs);
}    

public abstract class ModelReader : IDisposable
{
    private readonly IMessageHandler handler; // Should be initialized somewhere, e.g. in constructor

    public abstract Model Read();

    public event EventHandler<MessageAvailableEventArgs> MessageAvailable;

    protected virtual void RaiseError(string message)
    {
        MessageAvailaibleEventArgs eventArgs =
            new MessageAvailaibleEventArgs(TraceEventType.Error, message);
        this.handler.HandleMessage(this, eventArgs);
    }
}

So now you can use any implementation of message handler, for exmaple your event subscription one:

public class EventMessageHandler : IMessageHandler
{
    public event EventHandler<MessageAvailaibleEventArgs> MessageAvailable;

    public void HandleMessage(object sender, MessageAvailaibleEventArgs eventArgs)
    {
        var handler = this.MessageAvailable;
        if (handler != null)
        {
            handler(this, new MessageAvailaibleEventArgs(
                TraceEventType.Error, message);
        }
    }
}
Andrew Bezzub
Interesting, still potentially limits me to one sink.
sixlettervariables