I'm currently working on a program that allows for connections to multiple provider specific databases, so obviously I'm using the non-provider-specific classes (in System.Data.Common) to work with these various connections once the connection or data adapter has been instantiated through the use of the provider's implementation.
However, I wanted to make use of the RowUpdated and RowUpdating events that are supposedly a part of the DataAdapter base class according to MSDN (http://msdn.microsoft.com/en-us/library/6d1wk41s.aspx). But obviously this is not the case, because those events are only implemented through the provider-specific implementations of DbDataAdapter (like SqlDataAdapter, OleDbDataAdapter, etc.).
My main goal is to be able to handle those events through the use of the generic DbDataAdapter class, and it will be a big pain to have to cast things back to the derived classes of the provider-specific implementations for the data adapter.
Any slick ideas of doing this?
I did a lot of searching online, and really didn't find anything concrete besides just re-casting instances back to the derived classes to access those events.
So, with the use of Extension Methods, I added two new methods to the DbDataAdapter abstract class that will allow the adding of event handlers for those two specific events, and here is my implementation (edited 4/23/2013 for handling of instance or static handler methods):
using System;
using System.Data.Common;
using System.Reflection;
namespace Extensions
{
/// <summary>
/// Delegate event handler used with the <c>DbDataAdapter.RowUpdated</c> event.
/// </summary>
public delegate void RowUpdatedEventHandler(object sender, RowUpdatedEventArgs e);
/// <summary>
/// Delegate event handler used with the <c>DbDataAdapter.RowUpdating</c> event.
/// </summary>
public delegate void RowUpdatingEventHandler(object sender, RowUpdatingEventArgs e);
public static class DbDataAdapterExtension
{
private static EventInfo GetEvent(string eventName, Type type)
{
return type.GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
}
/// <summary>
/// Registers a <c>RowUpdatedEventHandler</c> with this instance's <c>RowUpdated</c> event.
/// </summary>
/// <param name="handler">The event handler to register for the event.</param>
/// <returns><c>true</c> if the event handler was successfully registered, otherwise <c>false</c>.</returns>
public static bool AddRowUpdatedHandler(this DbDataAdapter adapter, RowUpdatedEventHandler handler)
{
EventInfo updEvent = GetEvent("RowUpdated", adapter.GetType());
if (updEvent != null)
{
try
{
if (handler.Method.IsStatic)
{
updEvent.AddEventHandler(adapter, Delegate.CreateDelegate(updEvent.EventHandlerType, handler.Method));
}
else
{
updEvent.AddEventHandler(adapter, Delegate.CreateDelegate(updEvent.EventHandlerType, handler.Target, handler.Method));
}
return true;
}
catch { }
}
return false;
}
/// <summary>
/// Registers a <c>RowUpdatingEventHandler</c> with this instance's <c>RowUpdating</c> event.
/// </summary>
/// <param name="handler">The event handler to register for the event.</param>
/// <returns><c>true</c> if the event handler was successfully registered, otherwise <c>false</c>.</returns>
public static bool AddRowUpdatingHandler(this DbDataAdapter adapter, RowUpdatingEventHandler handler)
{
EventInfo updEvent = GetEvent("RowUpdating", adapter.GetType());
if (updEvent != null)
{
try
{
if (handler.Method.IsStatic)
{
updEvent.AddEventHandler(adapter, Delegate.CreateDelegate(updEvent.EventHandlerType, handler.Method));
}
else
{
updEvent.AddEventHandler(adapter, Delegate.CreateDelegate(updEvent.EventHandlerType, handler.Target, handler.Method));
}
return true;
}
catch { }
}
return false;
}
}
}
I'm using the base RowUpdatedEventArgs & RowUpdatingEventArgs for the event arguments that get returned to the delegate, so if you need provider-specific members that are only available through the provider defined classes that are derived from the above two base event args classes, then they will need to be cast to those classes. Otherwise, it works fine, and now I have provider-independent event handlers available from the DbDataAdapter class (which is how Microsoft should have implemented them to begin with).
Cheers!
Thank You - very useful!
Perhaps someone needs this in Visual Basic
Call it like this for example:
Imports YourProject.DbDataAdapterExtension
.
.
.
Dim HandleOnRowUpd As RowUpdatedEventHandler = AddressOf OnRowUpdated
Dim TrueFalse As Boolean = YourDataAdapter.AddRowUpdatedHandler(HandleOnRowUpd)
Imports System.Data.Common
Imports System.Reflection
Imports System.Runtime.CompilerServices
' Namespace Extensions
''' <summary>
''' Delegate event handler used with the <c>DbDataAdapter.RowUpdated</c> event.
''' </summary>
'''
Public Delegate Sub RowUpdatedEventHandler(sender As Object, e As RowUpdatedEventArgs)
''' <summary>
''' Delegate event handler used with the <c>DbDataAdapter.RowUpdating</c> event.
''' </summary>
'''
Public Delegate Sub RowUpdatingEventHandler(sender As Object, e As RowUpdatingEventArgs)
Public Module DbDataAdapterExtension
Sub New()
End Sub
Private Function GetEvent(eventName As String, type As Type) As EventInfo
Return type.GetEvent(eventName, BindingFlags.[Public] Or BindingFlags.Instance Or BindingFlags.DeclaredOnly)
End Function
''' <summary>
''' Registers a <c>RowUpdatedEventHandler</c> with this instance's <c>RowUpdated</c> event.
''' </summary>
''' <param name="handler">The event handler to register for the event.</param>
''' <returns><c>true</c> if the event handler was successfully registered, otherwise <c>false</c>.</returns>
'''
<Extension> Public Function AddRowUpdatedHandler(adapter As DbDataAdapter, handler As RowUpdatedEventHandler) As Boolean
Dim updEvent As EventInfo = GetEvent("RowUpdated", adapter.[GetType]())
If updEvent IsNot Nothing Then
Try
If handler.Method.IsStatic Then
updEvent.AddEventHandler(adapter, [Delegate].CreateDelegate(updEvent.EventHandlerType, handler.Method))
Else
updEvent.AddEventHandler(adapter, [Delegate].CreateDelegate(updEvent.EventHandlerType, handler.Target, handler.Method))
End If
Return True
Catch
End Try
End If
Return False
End Function
''' <summary>
''' Registers a <c>RowUpdatingEventHandler</c> with this instance's <c>RowUpdating</c> event.
''' </summary>
''' <param name="handler">The event handler to register for the event.</param>
''' <returns><c>true</c> if the event handler was successfully registered, otherwise <c>false</c>.</returns>
'''
<Extension> Public Function AddRowUpdatingHandler(adapter As DbDataAdapter, handler As RowUpdatingEventHandler) As Boolean
Dim updEvent As EventInfo = GetEvent("RowUpdating", adapter.[GetType]())
If updEvent IsNot Nothing Then
Try
If handler.Method.IsStatic Then
updEvent.AddEventHandler(adapter, [Delegate].CreateDelegate(updEvent.EventHandlerType, handler.Method))
Else
updEvent.AddEventHandler(adapter, [Delegate].CreateDelegate(updEvent.EventHandlerType, handler.Target, handler.Method))
End If
Return True
Catch
End Try
End If
Return False
End Function
End Module
' End Namespace