Visual Studio Tips 3

Next round of Visual Studio tips! 10 more for you! Here you can find the first and the second posts.

  1. Change solution version: you can change the Visual Studio solution version by opening the .SLN file and changing its header:


    Microsoft Visual Studio Solution File, Format Version 12.00
    VisualStudioVersion = 12.0.30110.0

    =Visual Studio 2013

    Microsoft Visual Studio Solution File, Format Version 12.00
    VisualStudioVersion = 14.0.23107.0

    =Visual Studio 2015

  2. Even if you didn’t declare a variable for the exception in a catch block, you can still see it by adding the special keyword $exception to the watch window:

    image
  3. Scroll bar options: the vertical scroll bar can show hints for a number of different things, or even display a map of the lines in its window. You can choose what it should be used for by right clicking on the scroll bar and clicking on either bar mode or map mode and selecting changes, marks, errors, etc:

    image
  4. Show line numbers: besides having version numbers on the status bar, you can also have them on the left; go to ToolsOptionsText Editor All Languages and select Line Numbers:
    image
  5. Move lines up or down: you can move an entire line up or down; just click on it and press <Alt>-<Up> or <Alt>-<Down>; you can also duplicate it with <Ctrl><C> + <Ctrl><V>, no need to actually select it all;
  6. Add search box to the toolbar: on the standard toolbar, click the button with the triangle pointing down, then select Add or Remove Buttons:

    imageand then choose Find; you will get a search box for your source code:
    image
  7. Open multiple browsers at once: you can open your page in different browsers at the same time, automatically, upon starting (<F5>) or debugging (<Ctrl><F5>); just select the down arrow next to the browser selection
    Browse WithThen select Browse With:
    Default Selected Browser

    And select several at once:
    Multiple Default Browser
  8. Change CodeLens options: right clicking on CodeLens will bring the options dialog:
    imagefrom which you can change what to display:
    image
  9. Change variable value from the debugger: if you have a variable, settable property, field or parameter in the watch window, you can change its value by clicking on the right column and entering an appropriate expression:

    image
  10. Add bookmarks: you can easily add bookmarks to lines of code by clicking <Ctrl>+<K>; to remove a bookmark, click <Ctrl>+<K> on the bookmarked line of code; <Ctrl>+<K> followed by <Ctrl>+<N> will navigate to the next bookmark and <Ctrl>+<K> followed by <Ctrl>+<P> to the previous one; adding the <Shift> key to <P> or <N> will do the same, but inside a solution folder.

And that’s all for now. Stay tuned for more!

Persisting SignalR Connections Across Page Reloads

I recently had the need to keep a SignalR connection even if the page would reload. As far as I know, this cannot be done out of the box, either with hubs or persistent connections. I looked it up, but could find no solid solution, so here is my solution!

First, we need to create a “session id” that is to be stored at the browser side. Mind you, this is not an ASP.NET session, nor a SignalR connection id, it’s something that uniquely identifies a session. To maintain sessions we normally use cookies, but my solution uses instead HTML5 session storage. I had to generate a session id, and there were several solutions available, from pseudo-GUIDs, to the SignalR connection id, but I ultimately decided to use the timestamp; here is it:

function getSessionId()

{

    var sessionId = window.sessionStorage.sessionId;

    

    if (!sessionId)

    {

        sessionId = window.sessionStorage.sessionId = Date.now();

    }

    

    return sessionId;

}

As you can see, this function first checks to see if the session id was created, by inspecting the sessionStorage object, and, if not, sets it.

Next, we need to have SignalR pass this session id on every request to the server. For that, I used $.connection.hub.qs, the query string parameters object:

$.connection.hub.qs = { SessionId: getSessionId() };

$.connection.hub.start().done(function ()

{

    //connection started

});

Moving on to the server-side, I used a static collection to store, for each session id, each SignalR connection id associated with it – one for each page request. The reasoning is, each page reload generates a new SignalR connection id, but the session id is always kept:

public sealed class NotificationHub : Hub

{

    internal const string SessionId = "SessionId";

 

    public static readonly ConcurrentDictionary<string, HashSet<string>> sessions = new ConcurrentDictionary<string, HashSet<string>>();

 

    public static IEnumerable<string> GetAllConnectionIds(string connectionId)

    {

        foreach (var session in sessions)

        {

            if (session.Value.Contains(connectionId) == true)

            {

                return session.Value;

            }

        }

 

        return Enumerable.Empty<string>();

    }

 

    public override Task OnReconnected()

    {

        this.EnsureGroups();

 

        return base.OnReconnected();

    }

 

    public override Task OnConnected()

    {

        this.EnsureGroups();

 

        return base.OnConnected();

    }

 

    private void EnsureGroups()

    {

        var connectionIds = null as HashSet<string>;

        var sessionId = this.Context.QueryString[SessionId];

        var connectionId = this.Context.ConnectionId;

 

        if (sessions.TryGetValue(sessionId, out connectionIds) == false)

        {

            connectionIds = sessions[sessionId] = new HashSet<string>();

        }

 

        connectionIds.Add(connectionId);

    }

}

As you can see, both on OnConnected as in OnReconnected, I add the current connection id to the collection (ConcurrentDictionary<TKey, TValue> to allow multiple concurrent accesses) indexed by the session id that I sent in the SignalR query string. Then, I have a method that looks in the collection for all connection id entries that are siblings of a given connection id. If more than one exists, it means that the page has reloaded, otherwise, there will be a one-to-one match between connection ids and session ids.

The final step is to broadcast a message to all the sibling connection ids – a waste of time because only one is still possibly active, but since we have no way of knowing, it has to be this way:

[HttpGet]

[Route("notify/{connectionId}/{message}")]

public IHttpActionResult Notify(string connectionId, string message)

{

    var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();

    var connectionIds = NotificationHub.GetAllConnectionIds(connectionId).ToList();

 

    context.Clients.Clients(connectionIds).MessageReceived(message);

 

    return this.Ok();

}

This Web API action method will get the context for our hub (NotificationHub), look up all of the sibling connection ids for the passed one, and then broadcast a message to all clients identified by these connection ids. It’s a way to send messages from outside of a page into a hub’s clients

Problems with this approach:

  • All tabs will get the same session id, but that also happens with cookies;
  • Although unlikely, it may be possible for two clients to get the same session id, which I implemented as the current timestamp; an easy fix would be, for example, to use a pseudo-GUID, the server-side session id, or even the SignalR connection id;
  • If the page reloads several times, there will be several connection id entries for the same session id – which will be kept throughout all reloads; no easy way to get around this, except possibly using some cache with expiration mechanism.

And that’s it. Enjoy!

Unity, Part 11: Integrating With Azure Application Insights

Another one for the Unity series.

Lately I’ve been playing with Azure Application Insights. Nice thing, even if – at least, for the moment – not as powerful as some of its contesters. A thing that came to my mind almost immediately was how to integrate it with IoC containers like Unity.

I already talked about how to use AOP techniques in Unity. This time I will leverage on that and explain how we can use this knowledge to add insights into our application transparently.

We need the Application Insights SDK, which is available at GitHub in source code and conveniently as a NuGet package (all you need is Microsoft.ApplicationInsights):

image

I implemented an HandlerAttribute that is also an implementation of ICallHandler. Inside of it, I call the intercepted method and then log it to Application Insights through the TelemetryClient, a part of the Application Insights APIs. I added an option to set the instrumentation key, which uniquely identifies our Application Insights account and shouldn’t be shared. If not supplied, it will default to whatever is in

TelemetryConfiguration.Active.InstrumentationKey. Finally, we can decide to have the call asynchronous (so as to not cause delays to our application) or synchronous.

Here is the code for the interception attribute:

[Serializable]

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]

public sealed class TelemetryCallHandlerAttribute : HandlerAttribute, ICallHandler

{

    #region Public constructors

    public TelemetryCallHandlerAttribute()

    {

    }

 

    public TelemetryCallHandlerAttribute(string instrumentationKey)

    {

        this.InstrumentationKey = instrumentationKey;

    }

 

    public string InstrumentationKey { get; set; }

 

    public bool Async { get; set; }

 

    #endregion

 

    #region Public override methods

    public override ICallHandler CreateHandler(IUnityContainer ignored)

    {

        return (this);

    }

    #endregion

 

    #region ICallHandler Members

 

    IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

    {

        TelemetryConfiguration config = null;

 

        if (string.IsNullOrWhiteSpace(this.InstrumentationKey) == true)

        {

            config = TelemetryConfiguration.Active;

        }

        else

        {

            config = TelemetryConfiguration.CreateDefault();

            config.InstrumentationKey = this.InstrumentationKey;

        }

 

        var telemetryClient = new TelemetryClient(config);

        var watch = Stopwatch.StartNew();

        var result = getNext()(input, getNext);

 

        var elapsedMilliseconds = watch.ElapsedMilliseconds;

        var exception = result.Exception;

        var returnValue = result.ReturnValue;

 

        var properties = new Dictionary<string, string>();

 

        for (var i = 0; i < input.Arguments.Count; ++i)

        {

            var key = input.Arguments.ParameterName(i);

            properties[key] = (input.Arguments[i] ?? string.Empty).ToString();

        }

 

        if (exception != null)

        {

            properties["$Exception"] = exception.Message;

        }

 

        if (returnValue != null)

        {

            properties["$ReturnValue"] = returnValue.ToString();

        }

 

        var metrics = new Dictionary<string, double>();

        metrics["ElapsedMilliseconds"] = elapsedMilliseconds;

 

        if (this.Async == false)

        {

            this.TrackEvent(telemetryClient, input.MethodBase.Name, properties, metrics);

        }

        else

        {

            this.TrackEventAsync(telemetryClient, input.MethodBase.Name, properties, metrics);

        }

 

        return (result);

    }

 

    private void TrackEvent(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

    {

        telemetryClient.TrackEvent(name, properties, metrics);

    }

 

    private async void TrackEventAsync(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

    {[Serializable]

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]

    public sealed class TelemetryCallHandlerAttribute : HandlerAttribute, ICallHandler

    {

        #region Public constructors

        public TelemetryCallHandlerAttribute()

        {

        }

 

        public TelemetryCallHandlerAttribute(string instrumentationKey)

        {

            this.InstrumentationKey = instrumentationKey;

        }

 

        public string InstrumentationKey { get; set; }

 

        public bool Async { get; set; }

 

        #endregion

 

        #region Public override methods

        public override ICallHandler CreateHandler(IUnityContainer ignored)

        {

            return (this);

        }

        #endregion

 

        #region ICallHandler Members

 

        IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

        {

            TelemetryConfiguration config = null;

 

            if (string.IsNullOrWhiteSpace(this.InstrumentationKey) == true)

            {

                config = TelemetryConfiguration.Active;

            }

            else

            {

                config = TelemetryConfiguration.CreateDefault();

                config.InstrumentationKey = this.InstrumentationKey;

            }

 

            var telemetryClient = new TelemetryClient(config);

            var watch = Stopwatch.StartNew();

            var result = getNext()(input, getNext);

 

            var elapsedMilliseconds = watch.ElapsedMilliseconds;

            var exception = result.Exception;

            var returnValue = result.ReturnValue;

 

            var properties = new Dictionary<string, string>();

 

            for (var i = 0; i < input.Arguments.Count; ++i)

            {

                var key = input.Arguments.ParameterName(i);

                properties[key] = (input.Arguments[i] ?? string.Empty).ToString();

            }

 

            if (returnValue != null)

            {

                properties["$ReturnValue"] = returnValue.ToString();

            }

 

            var metrics = new Dictionary<string, double>();

            metrics["ElapsedMilliseconds"] = elapsedMilliseconds;

 

            if (this.Async == false)

            {

                if (exception != null)

                {

                    properties["Name"] = input.MethodBase.Name;

                    this.TrackException(telemetryClient, exception, properties, metrics);

                }

                else

                {

                    this.TrackEvent(telemetryClient, input.MethodBase.Name, properties, metrics);

                }

            }

            else

            {

                if (exception != null)

                {

                    properties["Name"] = input.MethodBase.Name;

                    this.TrackExceptionAsync(telemetryClient, exception, properties, metrics);

                }

                else

                {

                    this.TrackEventAsync(telemetryClient, input.MethodBase.Name, properties, metrics);

                }

            }

 

            return (result);

        }

 

        private void TrackException(TelemetryClient telemetryClient, Exception ex, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            telemetryClient.TrackException(ex, properties, metrics);

        }

 

        private async void TrackExceptionAsync(TelemetryClient telemetryClient, Exception ex, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            await Task.Run(() => this.TrackException(telemetryClient, ex, properties, metrics));

        }

 

        private void TrackEvent(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            telemetryClient.TrackEvent(name, properties, metrics);

        }

 

        private async void TrackEventAsync(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            await Task.Run(() => this.TrackEvent(telemetryClient, name, properties, metrics));

        }

 

        #endregion

    }        await Task.Run(() => this.TrackEvent(telemetryClient, name, properties, metrics));

    }

 

    #endregion

}

It will track the event under the called method name, and will send along a string representation of all its arguments, result value, exception thrown (if any) and elapsed time (TelemetryClient.TrackEvent or TelemetryClient.TrackException).

A simple usage, without providing the instrumentation key, would be:

[TelemetryCallHandler]

public virtual BusinessResponse PerformBusinessOperation(int businessId, string arg)

{

    //...

}

If the InstrumentationKey property is not supplied, it must be set through TelemetryConfiguration.Active.InstrumentationKey:

TelemetryConfiguration.Active.InstrumentationKey = "my key";

Having it as an IInterceptionBehavior should be straightforward. Feel free to modify it to your liking!

Correlation Id Scope

A correlation id is an enterprise messaging pattern that helps bind together several messages. If they have the same correlation id, then they must be somehow related. Several servers and service bus frameworks, such as SharePoint and NServiceBus, use this concept.

We need correlation ids to be unique and apply to possibly several messages. I have been using a correlation scope class, presented below, to make sure of this:

public sealed class CorrelationScope : IDisposable

{

    [ThreadStatic]

    private static string correlationId;

    [ThreadStatic]

    private static CorrelationScope creator;

 

    private readonly Func<object> generator = () => Guid.NewGuid();

 

    public CorrelationScope(Func<object> gen)

    {

        if (correlationId == null)

        {

            this.generator = gen ?? this.generator;

            correlationId = this.generator().ToString();

            creator = this;

        }

    }

 

    public CorrelationScope() : this(null)

    {

    }

 

    public static string Current

    {

        get

        {

            return correlationId;

        }

    }

 

    void IDisposable.Dispose()

    {

        if (creator == this)

        {

            creator = null;

            correlationId = null;

        }

    }

}

A regular usage would be:

void AnotherFunc()

{

    using (new CorrelationScope())

    {

        var correlationId = CorrelationScope.Current;    //same as before

        //...

    }

}

 

void SomeFunc()

{

    using (new CorrelationScope())

    {

        var correlationId = CorrelationScope.Current;

        AnotherFunc();

 

        using (new CorrelationScope())

        {

            correlationId = CorrelationScope.Current;    //same as before

        }

    }

}

So, what we have is:

  • We instantiate a CorrelationScope instance, probably inside a using block; this will delimit the boundaries of our scope;
  • If no generator function is supplied, it uses Guid.NewGuid;
  • The scope will be unique per thread, regardless of the call stack, the current value will always be returned;
  • Nested CorrelationScopes will still retain the current value.

This pattern is also used by the TransactipnScope and Transaction.Current, but TransactipnScope allows starting a new scope or aborting if one already exists.

Well, it’s a simple thing, but maybe someone can find use for it! Winking smile

Elastic Object Implementation in .NET

I think it was Anoop Madhusudanan (aka, amazedsaint) who first coined the term “Elastic Object”. He even built an implementation, which you can find on GitHub and NuGet, which is very nice. Basically, it’s a class that allows adding members to its instances dynamically; this is a widely known pattern in scripting languages such as JavaScript, but not so easy to implement in .NET.

Now, I wanted to have something like this but also add some custom features that were missing, so I started something from scratch – yes, for the good or for the bad, my code is totally different from amazedsaint’s). Of course, both leverage the dynamic type, that’s where the whole idea came from.

Here are some examples of what we can do:

dynamic obj = new ElasticObject();

obj.A = 1;    //obj["A"] = 1;

obj.A.B = "1";

var props = TypeDescriptor.GetProperties(obj);    //"A"

 

dynamic obj = new ElasticObject(new { A = 1 });

int i = obj.A;

int ni = ~obj.A;

int ii = --obj.A;

bool b = obj.A;

 

dynmic obj = new ElasticObject(1);

var clone = obj.Clone();

(obj as INotifyPropertyChanged).PropertyChanged += (s, e) => { };

 

dynamic obj = new ElasticObject();

obj.A = 1;    //obj["A"] = 1;

obj.A.B = "1";

var path = obj.A.B["$path"];      //"A.B"

var parent = obj.A.B["$parent"];  //obj.A

var value = obj.A.B["$value"];    //"1"

var type = obj.A.B["$type"];      //string

Basically, I Inherit from DynamicObject and I use an internal dictionary for storing all dynamically assigned values, which will, in turn, be also ElasticObjects. I also allow for a “self value”, which will be the ElasticObject’s main value, in case no additional properties are supplied. I also provide implementations for some typical .NET interfaces, like INotifyPropertyChanged and ICloneable, and supply my own TypeDescriptionProvider which takes into account the dynamic properties and also a custom TypeConverter.

There are some provided properties that grant us access to the ElasticObject’s internals:

  • $Root: the root ElasticObject instance;
  • $Parent: the parent ElasticObject instance for the current one;
  • $Path: the full property path from the current object until the root;
  • $Value: the self value;
  • $Type: the type of the self value.

Without further discussion, here is the code for my ElasticObject class:

[Serializable]

[TypeConverter(typeof(ElasticObjectTypeConverter))]

[TypeDescriptionProvider(typeof(ElasticObjectTypeDescriptionProvider))]

public sealed class ElasticObject : DynamicObject, IDictionary<String, Object>, ICloneable, INotifyPropertyChanged

{

    private static readonly String [] SpecialKeys = new String[] { "$Path", "$Parent", "$Root", "$Value", "$Type" };

    private readonly IDictionary<String, Object> values = new Dictionary<String, Object>();

    private Object value;

    private ElasticObject parent;

 

    public ElasticObject() : this(null, null)

    {

    }

 

    internal ElasticObject(ElasticObject parent, Object value)

    {

        this.parent = parent;

        this.value = (value is ElasticObject) ? ((ElasticObject)value).value : value;

    }

 

    public ElasticObject(Object value) : this(null, value)

    {

    }

 

    public override String ToString()

    {

        if (this.value != null)

        {

            return (this.value.ToString());

        }

        else

        {

            var dict = this as IDictionary<String, Object>;

            return (String.Format("{{{0}}}", String.Join(", ", dict.Keys.Zip(dict.Values, (k, v) => String.Format("{0}={1}", k, v)))));

        }

    }

 

    public override Int32 GetHashCode()

    {

        if (this.value != null)

        {

            return (this.value.GetHashCode());

        }

        else

        {

            return (base.GetHashCode());

        }

    }

 

    public override Boolean Equals(Object obj)

    {

        if (Object.ReferenceEquals(this, obj) == true)

        {

            return (true);

        }

 

        var other = obj as ElasticObject;

 

        if (other == null)

        {

            return (false);

        }

 

        if (Object.Equals(other.value, this.value) == false)

        {

            return (false);

        }

 

        return (this.values.SequenceEqual(other.values));

    }

 

    public override IEnumerable<String> GetDynamicMemberNames()

    {

        return (this.values.Keys.Concat((this.value != null) ? TypeDescriptor.GetProperties(this.value).OfType<PropertyDescriptor>().Select(x => x.Name) : Enumerable.Empty<String>()));

    }

 

    public override Boolean TryBinaryOperation(BinaryOperationBinder binder, Object arg, out Object result)

    {

        if (binder.Operation == ExpressionType.Equal)

        {

            result = Object.Equals(this.value, arg);

            return (true);

        }

        else if (binder.Operation == ExpressionType.NotEqual)

        {

            result = !Object.Equals(this.value, arg);

            return (true);

        }

 

        return (base.TryBinaryOperation(binder, arg, out result));

    }

 

    public override Boolean TryUnaryOperation(UnaryOperationBinder binder, out Object result)

    {

        if (binder.Operation == ExpressionType.Increment)

        {

            if (this.value is Int16)

            {

                result = (Int16)value + 1;

                return (true);

            }

            else if (this.value is Int32)

            {

                result = (Int32)value + 1;

                return (true);

            }

            else if (this.value is Int64)

            {

                result = (Int64)value + 1;

                return (true);

            }

            else if (this.value is UInt16)

            {

                result = (UInt16)value + 1;

                return (true);

            }

            else if (this.value is UInt32)

            {

                result = (UInt32)value + 1;

                return (true);

            }

            else if (this.value is UInt64)

            {

                result = (UInt64)value + 1;

                return (true);

            }

            else if (this.value is Decimal)

            {

                result = (Decimal)value + 1;

                return (true);

            }

            else if (this.value is Single)

            {

                result = (Single)value + 1;

                return (true);

            }

            else if (this.value is Double)

            {

                result = (Double)value + 1;

                return (true);

            }

        }

        else if (binder.Operation == ExpressionType.Decrement)

        {

            if (this.value is Int16)

            {

                result = (Int16)value - 1;

                return (true);

            }

            else if (this.value is Int32)

            {

                result = (Int32)value - 1;

                return (true);

            }

            else if (this.value is Int64)

            {

                result = (Int64)value - 1;

                return (true);

            }

            else if (this.value is UInt16)

            {

                result = (UInt16)value - 1;

                return (true);

            }

            else if (this.value is UInt32)

            {

                result = (UInt32)value - 1;

                return (true);

            }

            else if (this.value is UInt64)

            {

                result = (UInt64)value - 1;

                return (true);

            }

            else if (this.value is Decimal)

            {

                result = (Decimal)value - 1;

                return (true);

            }

            else if (this.value is Single)

            {

                result = (Single)value - 1;

                return (true);

            }

            else if (this.value is Double)

            {

                result = (Double)value - 1;

                return (true);

            }

        }

        else if (binder.Operation == ExpressionType.Not)

        {

            if (this.value is Boolean)

            {

                result = !(Boolean)value;

                return (true);

            }

        }

        else if (binder.Operation == ExpressionType.OnesComplement)

        {

            if (this.value is Int16)

            {

                result = ~(Int16)value;

                return (true);

            }

            else if (this.value is Int32)

            {

                result = ~(Int32)value;

                return (true);

            }

            else if (this.value is Int64)

            {

                result = ~(Int64)value;

                return (true);

            }

            else if (this.value is UInt16)

            {

                result = ~(UInt16)value;

                return (true);

            }

            else if (this.value is UInt32)

            {

                result = ~(UInt32)value;

                return (true);

            }

            else if (this.value is UInt64)

            {

                result = ~(UInt64)value;

                return (true);

            }

        }

 

        return base.TryUnaryOperation(binder, out result);

    }

 

    public override Boolean TryInvokeMember(InvokeMemberBinder binder, Object[] args, out Object result)

    {

        var method = this.GetType().GetMethod(binder.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

 

        if (method == null)

        {

            foreach (var type in this.GetType().GetInterfaces())

            {

                method = type.GetMethod(binder.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

 

                if (method != null)

                {

                    break;

                }

            }

        }

 

        if (method != null)

        {

            result = method.Invoke(this, args);

 

            return (true);

        }

        else

        {

            return (base.TryInvokeMember(binder, args, out result));

        }

    }

 

    public override Boolean TryConvert(ConvertBinder binder, out Object result)

    {

        if (this.value != null)

        {

            if (binder.Type.IsInstanceOfType(this.value) == true)

            {

                result = this.value;

                return (true);

            }

            else if (binder.Type.IsEnum == true)

            {

                result = Enum.Parse(binder.Type, this.value.ToString());

                return (true);

            }

            else if ((typeof(IConvertible).IsAssignableFrom(binder.Type) == true) && (typeof(IConvertible).IsAssignableFrom(this.value.GetType()) == true))

            {

                result = Convert.ChangeType(this.value, binder.Type);

                return (true);

            }

            else if (binder.Type == typeof(String))

            {

                result = this.value.ToString();

                return (true);

            }

            else

            {

                var converter = TypeDescriptor.GetConverter(binder.Type);

 

                if (converter.CanConvertFrom(this.value.GetType()) == true)

                {

                    result = converter.ConvertFrom(this.value);

                    return (true);

                }

            }

        }

        else if (binder.Type.IsClass == true)

        {

            result = null;

            return (true);

        }

 

        result = null;

        return (false);

    }

 

    public override Boolean TrySetMember(SetMemberBinder binder, Object value)

    {

        (this as IDictionary<String, Object>)[binder.Name] = value;

 

        return (true);

    }

 

    public override Boolean TryGetMember(GetMemberBinder binder, out Object result)

    {

        if (this.value != null)

        {

            var prop = TypeDescriptor.GetProperties(this.value)[binder.Name];

 

            if (prop != null)

            {

                result = prop.GetValue(this.value);

                return (true);

            }

        }

 

        return (this.values.TryGetValue(binder.Name, out result));

    }

 

    public override Boolean TrySetIndex(SetIndexBinder binder, Object[] indexes, Object value)

    {

        if ((indexes.Count() != 1) || (indexes.First() == null))

        {

            return (false);

        }

 

        var key = indexes.First() as String;

 

        if (indexes.First() is Int32)

        {

            var index = (Int32)indexes.First();

 

            if (this.values.Count < index)

            {

                key = this.values.ElementAt(index).Key;

            }

        }

        else if (key == null)

        {

            return (false);

        }

 

        (this as IDictionary<String, Object>)[key] = value;

 

        return (true);

    }

 

    public override Boolean TryGetIndex(GetIndexBinder binder, Object[] indexes, out Object result)

    {

        if ((indexes.Count() != 1) || (indexes.First() == null))

        {

            result = null;

            return (false);

        }

 

        var key = indexes.First() as String;

 

        if (key != null)

        {

            if (this.value != null)

            {

                var prop = TypeDescriptor.GetProperties(this.value)[key];

 

                if (prop != null)

                {

                    result = prop.GetValue(this.value);

                    return (true);

                }

            }

 

            if (String.Equals("$parent", key, StringComparison.InvariantCultureIgnoreCase) == true)

            {

                result = this.parent;

                return (true);

            }

            else if (String.Equals("$value", key, StringComparison.InvariantCultureIgnoreCase) == true)

            {

                result = this.value;

                return (true);

            }

            else if (String.Equals("$type", key, StringComparison.InvariantCultureIgnoreCase) == true)

            {

                result = ((this.value != null) ? this.value.GetType() : null);

                return (true);

            }

            else if (String.Equals("$root", key, StringComparison.InvariantCultureIgnoreCase) == true)

            {

                var root = this;

 

                while (root != null)

                {

                    if (root.parent == null)

                    {

                        break;

                    }

 

                    root = root.parent;

                }

 

                result = root;

                return (true);

            }

            else if (String.Equals("$path", key, StringComparison.InvariantCultureIgnoreCase) == true)

            {

                var list = new LinkedList<string>();

 

                var p = this.parent;

                var previous = (Object)this;

 

                while (p != null)

                {

                    var kv = p.values.SingleOrDefault(x => (Object)x.Value == (Object)previous);

 

                    list.AddFirst(kv.Key);

 

                    previous = ((ElasticObject)kv.Value).parent;

                    p = p.parent;

                }

 

                result = String.Join(".", list);

                return (true);

            }

            else

            {

                return (this.values.TryGetValue(key, out result));

            }

        }

        else if (indexes.First() is Int32)

        {

            var index = (Int32)indexes.First();

 

            if (this.values.Count < index)

            {

                result = this.values.ElementAt(index).Value;

                return (true);

            }

        }

 

        result = null;

        return (false);

    }

 

    void IDictionary<String,Object>.Add(String key, Object value)

    {

        (this as IDictionary<String,Object>)[key] = value;

    }

 

    Boolean IDictionary<String,Object>.ContainsKey(String key)

    {

        return (this.GetDynamicMemberNames().Contains(key));

    }

 

    ICollection<String> IDictionary<String,Object>.Keys

    {

        get

        {

            return (this.GetDynamicMemberNames().ToList());

        }

    }

 

    Boolean IDictionary<String,Object>.Remove(String key)

    {

        return (this.values.Remove(key));

    }

 

    Boolean IDictionary<String,Object>.TryGetValue(String key, out Object value)

    {

        if (this.value != null)

        {

            var prop = TypeDescriptor.GetProperties(this.value)[key];

 

            if (prop != null)

            {

                value = prop.GetValue(this.value);

                return (true);

            }

        }

 

        return (this.values.TryGetValue(key, out value));

    }

 

    ICollection<Object> IDictionary<String,Object>.Values

    {

        get

        {

            return (this.values.Values.Concat((this.value != null) ? TypeDescriptor.GetProperties(this.value).OfType<PropertyDescriptor>().Select(x => x.GetValue(this.value)) : Enumerable.Empty<Object>()).ToList());

        }

    }

 

    Object IDictionary<String,Object>.this[String key]

    {

        get

        {

            if (this.value != null)

            {

                var prop = TypeDescriptor.GetProperties(this.value)[key];

 

                if (prop != null)

                {

                    return (prop.GetValue(this.value));

                }

            }

 

            return (this.values[key]);

        }

        set

        {

            if (value is ElasticObject)

            {

                this.values[key] = value;

                ((ElasticObject) value).parent = this;

            }

            else if (value == null)

            {

                this.values[key] = null;

            }

            else

            {

                this.values[key] = new ElasticObject(this, value);

            }

 

            this.OnPropertyChanged(new PropertyChangedEventArgs(key));

        }

    }

 

    private void OnPropertyChanged(PropertyChangedEventArgs e)

    {

        var handler = this.PropertyChanged;

 

        if (handler != null)

        {

            handler(this, e);

        }

    }

 

    void ICollection<KeyValuePair<String, Object>>.Add(KeyValuePair<String, Object> item)

    {

        (this as IDictionary<String, Object>)[item.Key] = item.Value;

    }

 

    void ICollection<KeyValuePair<String, Object>>.Clear()

    {

        this.values.Clear();

    }

 

    Boolean ICollection<KeyValuePair<String, Object>>.Contains(KeyValuePair<String, Object> item)

    {

        return (this.values.Contains(item));

    }

 

    void ICollection<KeyValuePair<String, Object>>.CopyTo(KeyValuePair<String, Object>[] array, Int32 arrayIndex)

    {

        this.values.CopyTo(array, arrayIndex);

    }

 

    Int32 ICollection<KeyValuePair<String, Object>>.Count

    {

        get

        {

            return (this.values.Count);

        }

    }

 

    Boolean ICollection<KeyValuePair<String,Object>>.IsReadOnly

    {

        get

        {

            return (this.values.IsReadOnly);

        }

    }

 

    Boolean ICollection<KeyValuePair<String,Object>>.Remove(KeyValuePair<String, Object> item)

    {

        return (this.values.Remove(item));

    }

 

    IEnumerator<KeyValuePair<String, Object>> IEnumerable<KeyValuePair<String,Object>>.GetEnumerator()

    {

        return (this.values.GetEnumerator());

    }

 

    IEnumerator IEnumerable.GetEnumerator()

    {

        return ((this as IDictionary<String, Object>).GetEnumerator());

    }

 

    public event PropertyChangedEventHandler PropertyChanged;

 

    Object ICloneable.Clone()

    {

        var clone = new ElasticObject(null, this.value) as IDictionary<String, Object>;

 

        foreach (var key in this.values.Keys)

        {

            clone[key] = (this.values[key] is ICloneable) ? (this.values[key] as ICloneable).Clone() : this.values[key];

        }

 

        return (clone);

    }

}

Granted, there may be some inefficiencies, I leave it to you, dear reader, the task to spot them and letting me know!

Now, the  TypeDescriptionProvider classes:

[Serializable]

public sealed class ElasticObjectTypeDescriptionProvider : TypeDescriptionProvider

{

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, Object instance)

    {

        return (new ElasticObjectTypeDescriptor(instance));

    }

}

 

[Serializable]

public sealed class ElasticObjectTypeDescriptor : CustomTypeDescriptor

{

    private readonly ElasticObject instance;

 

    public ElasticObjectTypeDescriptor(Object instance)

    {

        this.instance = instance as ElasticObject;

    }

 

    public override PropertyDescriptorCollection GetProperties()

    {

        if (this.instance != null)

        {

            return new PropertyDescriptorCollection((this.instance as IDictionary<String, Object>).Keys.Select(x => new ElasticObjectPropertyDescriptor(x)).ToArray());

        }

        else

        {

            return (base.GetProperties());

        }

    }

 

    public override TypeConverter GetConverter()

    {

        return (new ElasticObjectTypeConverter());

    }

 

    public override AttributeCollection GetAttributes()

    {

        return (new AttributeCollection(new SerializableAttribute()));

    }

}

And a custom converter:

[Serializable]

public sealed class ElasticObjectTypeConverter : TypeConverter

{

    public override Boolean CanConvertFrom(ITypeDescriptorContext context, Type sourceType)

    {

        return (true);

    }

 

    public override Boolean CanConvertTo(ITypeDescriptorContext context, Type destinationType)

    {

        if (destinationType == typeof(ElasticObject))

        {

            return (true);

        }

        else

        {

            return (base.CanConvertTo(context, destinationType));

        }

    }

 

    public override Object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)

    {

        if (value is ElasticObject)

        {

            return (value);

        }

        else

        {

            return (new ElasticObject(null, value));

        }

    }

 

    public override Object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, Object value, Type destinationType)

    {

        if (destinationType == typeof(ElasticObject))

        {

            return (this.ConvertFrom(context, culture, value));

        }

        else

        {

            return base.ConvertTo(context, culture, value, destinationType);

        }

    }

 

    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, Object value, Attribute[] attributes)

    {

        var provider = TypeDescriptor.GetProvider(value) as TypeDescriptionProvider;

        var descriptor = provider.GetTypeDescriptor(value);

        return (descriptor.GetProperties(attributes));

    }

 

    public override Boolean GetPropertiesSupported(ITypeDescriptorContext context)

    {

        return (true);

    }

 

    public override Object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)

    {

        dynamic obj = new ElasticObject();

 

        foreach (var key in propertyValues.Keys)

        {

            obj[key.ToString()] = propertyValues[key];

        }

 

        return (obj);

    }

 

    public override Boolean GetCreateInstanceSupported(ITypeDescriptorContext context)

    {

        return (true);

    }

 

    public override Boolean GetStandardValuesSupported(ITypeDescriptorContext context)

    {

        return (false);

    }

}

That’s about it. Have fun and make sure you send me your thoughts!

Data Platform Airlift 2015

Today I had the great pleasure to attend the Data Platform Airlift 2015 event, at Microsoft Portugal! Moreover, I presented a session together with Luís Calado on what’s new in the Azure world! My part was specifically about DocumentDB, one of Microsoft’s offers in the field of NoSQL.

Videos and materials for all presentations will be made available on Channel 9 in the next days, but, in the meantime, you can find my slide deck here.

As always, looking forward for your feedback!

Type Converter to Type

What happens when you need to pass a type as a string? Well, if the string is an assembly qualified type name, and if the assembly is either in Global Assembly Cache or in a known location – such as the Bin folder or a probed path – you’re likely to succeed. But, what if that isn’t the case? Well, we can follow these steps:

  1. Try to locate the type the usual way, by calling Type.GetType;
  2. Look the type in the current executing, entry and calling assemblies;
  3. Look it up in all of the loaded assemblies for the current domain (could be a lot);
  4. Hook up the TypeResolve event of the current app domain (I am not going to show that).

Normally, in .NET we use type converters for doing this kind of stuff in configuration files or ASP.NET markup. Following that, a type converter for turning a string into a type might look like this:

public sealed class TypeTypeConverter : TypeConverter

{

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)

    {

        if (sourceType == typeof (string))

        {

            return true;

        }

 

        return base.CanConvertFrom(context, sourceType);

    }

 

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)

    {

        if (destinationType == typeof (Type))

        {

            return true;

        }

 

        return base.CanConvertTo(context, destinationType);

    }

 

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)

    {

        var typeName = value.ToString();

 

        //first attempt

        var type = Type.GetType(typeName, false, true);

 

        if (type == null)

        {

            //second attempt

            var assemblies = new[] { Assembly.GetEntryAssembly(), Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly() }.Where(asm => asm != null).Distinct();

 

            foreach (var assembly in assemblies)

            {

                type = assembly.GetType(typeName, false, true);

 

                if (type != null)

                {

                    break;

                }

            }

        }

 

        if (type == null)

        {

            //third attempt

            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

 

            foreach (var assembly in assemblies)

            {

                type = assembly.GetType(typeName, false, true);

 

                if (type != null)

                {

                    break;

                }

            }

        }

 

        return type;

    }

 

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)

    {

        if (destinationType == typeof (Type))

        {

            return this.ConvertFrom(context, culture, value);

        }

 

        return base.ConvertTo(context, culture, value, destinationType);

    }

}

We just need to apply a TypeConverterAttribute attribute to a property and the magic occurs naturally:

[TypeConverter(typeof(TypeTypeConverter))]

public Type Type { get; set; }

Or if we need to invoke it explicitly:

var converter = TypeDescriptor.GetConverter(typeof(Type));

var type = converter.ConvertFromString("MyNamespace.MyClass");

And that’s it. Keep in mind that it is always preferable that you specify an assembly qualified type name, but if that can’t be done, this might come in handy.

Custom .NET Data Provider

The .NET framework offers a provider model for ADO.NET. It is possible to register ADO.NET providers that can be used to dynamically create connections and other kinds of objects:

var myProvider = DbProviderFactories.GetFactory("MyProvider");

 

using (var con = myProvider.CreateConnection())

using (var cmd = con.CreateCommand())

{

    con.ConnectionString = "My connection string";

    con.Open();

 

    //do something with cmd

}

The provider name was also mandatory for connection strings specified in configuration files:

<connectionStrings>

    <add connectionString="My connection string" name="MyConnection" providerName="MyProvider"/>

</connectionStrings>

var provider = DbProvider0Factories.GetFactory(ConfigurationManager.ConnectionStrings["MyConnection"].ProviderName);

Before .NET 4.5, this was controlled through the DbProviderFactories section of Machine.config, and it was possible to register our own in a local Web/App.config file. Presently, the built-in providers – SqlClientFactory, OdbcFactory, OleDbFactory and OracleClientFactory – are no longer configured through a file, but are set automatically by the framework, therefore, cannot be easily changed. The registered providers can be inspected through the GetFactoryClasses() method of DbProviderFactories:

var providers = DbProviderFactories.GetFactories();

Using some reflection, however, it is possible to switch a built-in provider for a custom one. Why would you want to do that? Well, for example, to add custom profiling and logging to a connection or command.

The most important base classes in the ADO.NET model are DbConnection and DbCommand. DbCommand can be obtained through an existing DbConnection or from a DbProviderFactory. So, if we want to intercept DbCommand. we need to create custom DbConnection and DbCommand classes:

public class WrapperCommand : DbCommand

{

    private static readonly PropertyInfo canRaiseEventsProp = typeof(Component).GetProperty("CanRaiseEvents", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty);

    private static readonly MethodInfo disposeMethod = typeof(Component).GetMethod("Dispose", BindingFlags.NonPublic | BindingFlags.Instance);

    private static readonly MethodInfo getServiceMethod = typeof(Component).GetMethod("GetService", BindingFlags.NonPublic | BindingFlags.Instance);

 

    private readonly DbCommand original;

 

    public WrapperCommand(DbCommand original)

    {

        this.original = original;

    }

 

    public override void Prepare()

    {

        this.original.Prepare();

    }

 

    public override string CommandText

    {

        get { return this.original.CommandText; }

        set { this.original.CommandText = value; }

    }

 

    public override int CommandTimeout

    {

        get { return this.original.CommandTimeout; }

        set { this.original.CommandTimeout = value; }

    }

 

    public override CommandType CommandType

    {

        get { return this.original.CommandType; }

        set { this.original.CommandType = value; }

    }

 

    public override UpdateRowSource UpdatedRowSource

    {

        get { return this.original.UpdatedRowSource; }

        set { this.original.UpdatedRowSource = value; }

    }

 

    protected override DbConnection DbConnection

    {

        get { return this.original.Connection; }

        set { this.original.Connection = value; }

    }

 

    protected override DbParameterCollection DbParameterCollection

    {

        get { return this.original.Parameters; }

    }

 

    protected override DbTransaction DbTransaction

    {

        get { return this.original.Transaction; }

        set { this.original.Transaction = value; }

    }

 

    public override bool DesignTimeVisible

    {

        get { return this.original.DesignTimeVisible; }

        set { this.original.DesignTimeVisible = value; }

    }

 

    public override void Cancel()

    {

        this.original.Cancel();

    }

 

    protected override DbParameter CreateDbParameter()

    {

        return this.original.CreateParameter();

    }

 

    protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)

    {

        return this.original.ExecuteReader(behavior);

    }

 

    public override int ExecuteNonQuery()

    {

        return this.original.ExecuteNonQuery();

    }

 

    public override object ExecuteScalar()

    {

        return this.original.ExecuteScalar();

    }

 

    protected override bool CanRaiseEvents

    {

        get

        {

            return (bool)canRaiseEventsProp.GetValue(this.original, null);

        }

    }

 

    public override ObjRef CreateObjRef(Type requestedType)

    {

        return this.original.CreateObjRef(requestedType);

    }

 

    protected override void Dispose(bool disposing)

    {

        disposeMethod.Invoke(this.original, new object[] { disposing });

    }

 

    protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)

    {

        return this.original.ExecuteReaderAsync(behavior, cancellationToken);

    }

 

    public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)

    {

        return this.original.ExecuteNonQueryAsync(cancellationToken);

    }

 

    public override Task<object> ExecuteScalarAsync(CancellationToken cancellationToken)

    {

        return this.original.ExecuteScalarAsync(cancellationToken);

    }

 

    protected override object GetService(Type service)

    {

        return getServiceMethod.Invoke(this.original, new object[] { service });

    }

 

    public override object InitializeLifetimeService()

    {

        return this.original.InitializeLifetimeService();

    }

 

    public override ISite Site

    {

        get { return this.original.Site; }

        set { this.original.Site = value; }

    }

}

 

public class WrapperConnection : DbConnection

{

    private static readonly MethodInfo disposeMethod = typeof(Component).GetMethod("Dispose", BindingFlags.NonPublic | BindingFlags.Instance);

    private static readonly MethodInfo getServiceMethod = typeof(Component).GetMethod("GetService", BindingFlags.NonPublic | BindingFlags.Instance);

    private static readonly PropertyInfo canRaiseEventsProp = typeof(Component).GetProperty("CanRaiseEvents", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty);

    private static readonly MethodInfo onStateChangeMethod = typeof(DbConnection).GetMethod("OnStateChange", BindingFlags.NonPublic | BindingFlags.Instance);

 

    private readonly DbConnection original;

 

    public WrapperConnection(DbConnection original)

    {

        this.original = original;

    }

 

    protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)

    {

        return this.original.BeginTransaction(isolationLevel);

    }

 

    public override void Close()

    {

        this.original.Close();

    }

 

    public override void ChangeDatabase(string databaseName)

    {

        this.original.ChangeDatabase(databaseName);

    }

 

    public override void Open()

    {

        this.original.Open();

    }

 

    public override string ConnectionString

    {

        get { return this.original.ConnectionString; }

        set { this.original.ConnectionString = value; }

    }

 

    public override string Database

    {

        get { return this.original.Database; }

    }

 

    public override ConnectionState State

    {

        get { return this.original.State; }

    }

 

    public override string DataSource

    {

        get { return this.original.DataSource; }

    }

 

    public override string ServerVersion

    {

        get { return this.original.ServerVersion; }

    }

 

    protected override DbCommand CreateDbCommand()

    {

        return new WrapperCommand(this.original.CreateCommand());

    }

 

    public override ISite Site

    {

        get { return this.original.Site; }

        set { this.original.Site = value; }

    }

 

    protected override DbProviderFactory DbProviderFactory

    {

        get { return WrapperProviderFactory.Instance; }

    }

 

    public override object InitializeLifetimeService()

    {

        return this.original.InitializeLifetimeService();

    }

 

    protected override void Dispose(bool disposing)

    {

        disposeMethod.Invoke(this.original, new object[] { disposing });

    }

 

    protected override object GetService(Type service)

    {

        return getServiceMethod.Invoke(this.original, new object[] { service });

    }

 

    protected override bool CanRaiseEvents

    {

        get { return (bool)canRaiseEventsProp.GetValue(this.original, null); }

    }

 

    public override ObjRef CreateObjRef(Type requestedType)

    {

        return this.original.CreateObjRef(requestedType);

    }

 

    public override int ConnectionTimeout

    {

        get { return this.original.ConnectionTimeout; }

    }

 

    public override void EnlistTransaction(System.Transactions.Transaction transaction)

    {

        this.original.EnlistTransaction(transaction);

    }

 

    public override DataTable GetSchema()

    {

        return this.original.GetSchema();

    }

 

    public override DataTable GetSchema(string collectionName)

    {

        return this.original.GetSchema(collectionName);

    }

 

    public override DataTable GetSchema(string collectionName, string[] restrictionValues)

    {

        return this.original.GetSchema(collectionName, restrictionValues);

    }

 

    protected override void OnStateChange(StateChangeEventArgs stateChange)

    {

        onStateChangeMethod.Invoke(this.original, new object[] { stateChange });

    }

 

    public override Task OpenAsync(CancellationToken cancellationToken)

    {

        return this.original.OpenAsync(cancellationToken);

    }

 

    public override event StateChangeEventHandler StateChange

    {

        add { this.original.StateChange += value; }

        remove { this.original.StateChange -= value; }

    }

}

Our implementations need to take preexisting objects, to which they will delegate the actual work. Some reflection is needed to access non-public members. In order to make some use of it, we could add some virtual methods or events, for example, to add custom code before and after SQL commands are executed.

Next we need some provider factory that can be registered and replace the default functionality of some provider, here’s a possible implementation:

public sealed class WrapperProviderFactory : DbProviderFactory

{

    private static readonly FieldInfo providerTableField = typeof(DbProviderFactories).GetField("_providerTable", BindingFlags.NonPublic | BindingFlags.Static);

    private static readonly FieldInfo instanceField = typeof(WrapperProviderFactory).GetField("Instance", BindingFlags.Public | BindingFlags.Static);

 

    public static readonly WrapperProviderFactory Instance;

 

    private readonly DbProviderFactory original;

 

    private WrapperProviderFactory(DbProviderFactory original)

    {

        this.original = original;

 

        DbProviderFactories.GetFactoryClasses();

 

        var dt = providerTableField.GetValue(null) as DataTable;

        var row = dt.Rows.Find(original.GetType().Namespace);

        dt.Columns["AssemblyQualifiedName"].ReadOnly = false;

        row["AssemblyQualifiedName"] = typeof(WrapperProviderFactory).AssemblyQualifiedName;

        dt.Columns["AssemblyQualifiedName"].ReadOnly = true;

    }

 

    public static void Initialize(DbProviderFactory original)

    {

        instanceField.SetValue(null, new WrapperProviderFactory(original));

    }

 

    public override DbConnection CreateConnection()

    {

        return new WrapperConnection(this.original.CreateConnection());

    }

 

    public override DbCommand CreateCommand()

    {

        return new WrapperCommand(this.original.CreateCommand());

    }

 

    //other method overrides

}

The requirements for a custom DbProviderFactory are simple:

If you are curious, the first call to GetFactoryClasses() is required so that .NET can setup internally its structures and populate them with the default providers.

Our delegation first has the framework create the list of providers and then modify it so as to replace the provider that we want. We will need to initialize our provider by passing it an existing provider (Initialize) before doing something else:

WrapperProviderFactory.Initialize(SqlClientFactory.Instance);

And that’s it. As soon as you call this, your wrapper implementation becomes registered, and whenever the named provider is retrieved, .NET will return this implementation.

Ongoing Series of Posts

So, for your – and my – information, these are the post series currently going on in my blog:

Lesser-Known NHibernate Features: Executable HQL

var records = session.CreateQuery("update Person p set p.Email = p.Email + p.Username + '@somedomain.com' where p.Email is null").ExecuteUpdate();

What happens when you need to bulk change a lot of records on the database? The unwary novice might be tempted to load data from the database into class instances, change them and then either rely on change tracking to eventually make the changes persistent or even worse, explicitly do an update on every possibly changed entity. The non-novice readers should now rolling their eyes.

It so happens that NHibernate offers a great alternative in the form of executable HQL. Basically, it is HQL for doing bulk changes: inserts, updates and deletes.

HQL Inserts have a small gotcha: they need to come from selects. Here’s an example:

var records = session.CreateQuery("insert into Account (Name, Email, Birthday) select p.Name, p.Email, p.Birthday from Person").ExecuteUpdate();

Here is an update:

var records = session.CreateQuery("update Person p set p.Email = p.Email + p.Username + '@somedomain.com' where p.Email is null").ExecuteUpdate();

Two problems arise:

  • Cannot do joins with updates;
  • Does not update version properties.

The second one is easy to solve:

var records = session.CreateQuery("update versioned Person p set p.Email = p.Email + p.Username + '@somedomain.com' where p.Email is null").ExecuteUpdate();

Noticed the versioned keyword? This tells NHibernate to do the right thing: update the version on each affected entity, of the entity is versioned.

The final one is deletes:

var records = session.CreateQuery("delete Product p where size(p.Sales) = 0").ExecuteUpdate();

The only problem with this is that it does not cascade. You need to find another solution.

A final word on this: you can, of course, specify parameters in your queries, like in the following example.

var records = session.CreateQuery("delete Product p where p.Price = :price").SetParameter("price", 0).ExecuteUpdate();