Saturday, December 19, 2015

Group Regex Replace in Visual Studio

Wow, I cannot believe that I did not know about this until now!

We all know that you can use regular expressions to search for things in a document, and we all know that you can replace those matches, but did you know that you can use group matches from your regex in the replace value? Because you can, and it is amazingly useful!

Example: Your class has properties with one attribute containing a value, and you want to add another attribute with the same value.

Find: \[Description\("(\w+)"\)\]

public class MyClass
{
    [Description("A"), DisplayName("A")]
    public string A { get; set; }
 
    [Description("B")]
    public string B { get; set; }
 
    [Description("C")]
    public string C { get; set; }
}

Replace: [Description("$1"), DisplayName("$1")

public class MyClass
{
    [Description("A"), DisplayName("A")]
    public string A { get; set; }
 
    [Description("B"), DisplayName("B")]
    public string B { get; set; }
 
    [Description("C"), DisplayName("C")]
    public string C { get; set; }
}

Enjoy,
Tom

Wednesday, December 16, 2015

2015 Retrospective

Wow! This is my 200th post, and the exact 7 year anniversary of when I started blogging!

Blog

Blogging for seven years straight has been quite a challenge, but it has also been one of most rewarding things that I have ever done. It has become a regular occurrence for me to answer questions at work with "oh, I have a blog post about that!"

I work best when I have deadlines and quotas, and I am very happy to have written three posts per month this year. I think that this pace has been perfect; enough to keep me busy and share plenty of information, but still not so much that I didn't have time to really fill out the content.

I intend to continue writing three posts per month in 2016.

QQ-Cast

Unfortunately, despite how much fun we had in 2014, the QQ-Cast got put on hiatus for most of 2015. This was not something that Jordan and I wanted to do, but as we say on the show "life got in the way." Jordan became a father, and I...well we will get to that in a moment.

Good news dear listener, we're back! My friend Zach Mayer and I have just started recording again. We are continuing the tradition of being iterative, so we will be making several tweaks to the show format. I'm very happy to be recording again, and can't wait to see how 2016 goes.

Professional

2015 was yet another crazy year for me professionally, and I wouldn't have it any other way! My company launched an amazing number of products, and I am very proud to have directly contributed to several of those launches.

My team and I have been focusing heavily automation and performance testing, and it is an absolute blast! We get to engage with engineers from around the company, we get to play with a diverse set of tech stacks, and I just love every minute of it.

At the time of writing this we have openings for C#, C++, and Node.js...so come work with us!

Personal

2015 has been a rough year for me personally. Tragedy struck my wife and I in April, and we have been struggling to recover ever since. Frankly, I cannot help but be glad that 2015 is over, and I can only hope that 2016 goes...better.

On the plus side, after being on the bench for four years, I have finally started to play soccer again! It's been an absolutely blast to just run around and kick the ball again. What's next? Rock climbing? Sailing? Spelunking? Let's find out!

Thanks again,
Tom

Sunday, December 13, 2015

WebSocket4Net Extensions: OpenAsync

I recently talked about .NET WebSocket Libraries, and how I like using WebSocket4Net as a .NET WebSocket client. That library is great, but really wanted it to have two additional features:

  1. Retry logic for opening a connection.
  2. An OpenAsync method.

...so I created an extension method that does both!

WebSocket4Net Extensions

public static class WebSocketExtensions
{
    public static async Task OpenAsync(
        this WebSocket webSocket,
        int retryCount = 5,
        CancellationToken cancelToken = default(CancellationToken))
    {
        var failCount = 0;
        var exceptions = new List<Exception>(retryCount);
 
        var openCompletionSource = new TaskCompletionSource<bool>();
        cancelToken.Register(() => openCompletionSource.TrySetCanceled());
 
        EventHandler openHandler = (s, e) => openCompletionSource.TrySetResult(true);
 
        EventHandler<ErrorEventArgs> errorHandler = (s, e) =>
        {
            if (exceptions.All(ex => ex.Message != e.Exception.Message))
            {
                exceptions.Add(e.Exception);
            }
        };
 
        EventHandler closeHandler = (s, e) =>
        {
            if (cancelToken.IsCancellationRequested)
            {
                openCompletionSource.TrySetCanceled();
            }
            else if (++failCount < retryCount)
            {
                webSocket.Open();
            }
            else
            {
                var exception = exceptions.Count == 1
                    ? exceptions.Single()
                    : new AggregateException(exceptions);
 
                var webSocketException = new WebSocketException(
                    "Unable to connect", 
                    exception);
 
                openCompletionSource.TrySetException(webSocketException);
            }
        };
 
        try
        {
            webSocket.Opened += openHandler;
            webSocket.Error += errorHandler;
            webSocket.Closed += closeHandler;
 
            webSocket.Open();
 
            await openCompletionSource.Task.ConfigureAwait(false);
        }
        finally
        {
            webSocket.Opened -= openHandler;
            webSocket.Error -= errorHandler;
            webSocket.Closed -= closeHandler;
        }
    }

Enjoy,
Tom

Real Time Web Analytics