views:

373

answers:

4

This seems like a really basic thing that I'm doing, yet I'm tearing my hair out trying to make it work.

My situation is this: I have a project which contains a large number of lookup tables, and I have all of these lookup tables represented in a single typed DataSet, which contains TableAdapters for each lookup. I've designed an editor for these lookup tables, which should allow editing of one of these at a time. My front-end is written in VB and WinForms, the back-end is a SOAP web service; I can successfully pass the changes to the DataSet back to the web service, but can't find a way to use a TableAdapter to update the single table that has been changed.

What I'm trying to do is instantiate the appropriate TableAdapter for the updated DataTable by sending the name of the table back to the web service along with the DataSet, then referring to the TableAdapter with a dynamic name. The normal way to instantiate a TableAdapter is this:

Dim ta As New dsLookupsTableAdapters.tlkpMyTableTableAdapter

What I'd like to do is this, but of course it doesn't work:

strTableName = "tlkpMyTable"
Dim ta As New dsLookupsTableAdapters(strTableName & "TableAdapter")

Is there any way to achieve this, or am I taking the wrong approach altogether? My other alternative is to write separate code for each table, which I'd prefer to avoid!

+1  A: 

It's pretty easy to create types at runtime given the (string) type name.

Here's a self-contained VB class which illustrates one way to do it: use System.Activator.CreateInstance to create instances of types using a string representation of the type name. Then you can cast it to a DataAdapter base class and use it like any other DataAdapter.

Public Class dsLookupsTableAdapters

    Public Function CreateInstance(ByVal strName As String) As Object
        CreateInstance = Nothing

        For Each a As System.Reflection.Assembly In System.AppDomain.CurrentDomain.GetAssemblies()
            Try
                Dim strAssemblyName As String() = a.FullName.Split(New Char() {","c})
                Dim strNameTemp As String = strAssemblyName(0) & "." & strName
                Dim instance As Object = System.Activator.CreateInstance(a.FullName, strNameTemp)
                If instance IsNot Nothing Then
                    Dim handle As System.Runtime.Remoting.ObjectHandle
                    handle = CType(instance, System.Runtime.Remoting.ObjectHandle)
                    Dim o As Object = handle.Unwrap()
                    CreateInstance = o
                    Exit For
                End If
            Catch ex As System.Exception
                Continue For ' ignore exception, means type isn't there
            End Try
        Next
    End Function


    Public Class tlkpMyTableTableAdapter
        Inherits System.Data.Common.DataAdapter

    End Class


    Public Sub Test()
        ' define type name. note that, in this sample, tlkpMyTableTableAdapter is a nested
        ' class and dsLookupsTableAdapters is the containing class, hence the "+". If, however,
        ' dsLookupsTableAdapters is a namespace, replace the "+" with a "."
        Dim typeName As String = "dsLookupsTableAdapters+tlkpMyTableTableAdapter"
        Dim adapter As System.Data.Common.DataAdapter
        Dim o As Object = CreateInstance(typeName)
        adapter = CType(o, System.Data.Common.DataAdapter)

    End Sub

End Class
Justin Grant
Sorry not to get back to you sooner - we're in the middle of moving house at the moment and I haven't had a chance to try it out! Will do so ASAP.
Billious
No problem. FYI, your bounty expires in half an hour, so if you want to test this code out, you might want to do it soon... :-)
Justin Grant
Hi Justin, just letting you know I haven't forgotten about you - I've tried the code, but haven't been able to get it to work yet (mainly due to my own ignorance, I think). I've had to focus on another part of my project for a bit, but will try to give it another go this week.
Billious
+1  A: 

Not sure I 100% understand, do you have a single DataTable in your DataSet, or one DataTable per lookup table?

Anyway, perhaps you could you this approach to filter by lookup table?

Si
No, I have multiple DataTables (with TableAdapters) in my DataSet. When the application is loaded, I load all the lookup tables into memory at the same time using this DataSet. However, when updating the lookup tables, I want to be able to do this by sending back only the modifications to a given table and using the appropriate TableAdapter for the update statement.
Billious
+1  A: 

If you are using VB.Net 2008, then use the tableadaptermanager (http://msdn.microsoft.com/en-us/library/bb384426.aspx). I think this would be much easier to code against :)

Wade

Wade73
Sorry, although I'm using VS2008, the server I'm writing for only has .NET Framework v2.0, whereas TableAdapterManager is only available in v3.5 and higher. Would be a useful tool though!
Billious
+2  A: 

You can use Activator to create an instance of your TableAdapter from its string name, just like you want:

object adapter = Activator.CreateInstance(Type.GetType("My.Namespace.MyDataSetTableAdapters." + myTable.Name + "TableAdapter"));

Then, because TableAdapters don't have a common interface, you should use reflection to call its Update method:

adapter.GetType().GetMethod("Update").Invoke(adapter, null);

http://msdn.microsoft.com/en-us/library/system.type.getmethod.aspx

This is from memory, but roughly close enough. You can also use GetProperty to get the connection property and set it as required.

Ilia Jerebtsov
Since it is VB.NET, avoid the reflection and use CallByName(adapter, "Update", CallType.Method, Nothing)
AMissico
Thanks guys, that did the trick. Sorry it's taken me so long to get back to this - I've been busy with other aspects of the project that were more urgent. Unfortunately I can't seem to accept the answer though - any ideas why?
Billious
Hmmm - just found out that you can't accept an answer after a bounty has expired, which is a bit annoying. Ilia, your answer was the one that I would have accepted, but it came after the bounty had expired - thanks anyway!
Billious