Question

I tried to make some experiment today. I have an application that uses untyped datatables as the model entities. They are all made like:

Imports System.Data
Imports System.Runtime.Serialization
Imports System.ComponentModel

<DesignerCategory("Code"), system.Serializable()>
Partial Public Class SomeTable1
    Inherits DataTable

#Region
    Public Const TABLE_NAME As String = "SomeTable1"

    Public Const FIELD_SomeField1 As String = "SomeField1"
    Public Const FIELD_SomeField2 As String = "SomeField2"
#End Region

    Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        MyBase.New(info, context)
    End Sub

    Public Sub New()
        MyBase.New()

        With Columns
            .Add(FIELD_SomeField1, GetType(System.String)).DefaultValue = String.Empty
            .Add(FIELD_SomeField2, GetType(System.Double)).DefaultValue = 0
        End With

        Dim keys(1) As DataColumn
        keys(0) = Columns(FIELD_SomeField1)
        TableName = TABLE_NAME
        PrimaryKey = keys
    End Sub
End Class

I'm currently working with EF, so in my razzle, I wrote something like this (yeah, it's vb):

Partial Public Class SomeTable1
    Inherits DataTable

    <Key()>
    Friend Property SomePK1 As DataColumn

    <Required(ErrorMessage:="SomeField1 is required.")>
    <DataType(DataType.Text)>
    Friend Property SomeField1 As DataColumn

    <Required()>
    <DataType(DataType.DateTime)>
    Friend Property SomeField2 As DataColumn
    ...

    Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        MyBase.New(info, context)
    End Sub

    Public Sub New()
        MyBase.New()

        SomeField2 = Date.Now
    End Sub
End Class

I was dreaming on making something equivalent to the former dt and being completely compatible with the current data engine.

And then the type conversion error (system date to datacolumn) broke my hopes. I must admit that has been a hard weekend :)

So before I completely discard the change, Is there any way of writing a Typed datatable so it's equivalent to the code above but with some new goodies? That's so ancient way of programming I can't find anything on the net. Thanks in advance.

Was it helpful?

Solution 2

I found how to do what I wanted. Perhaps involves some work, but it works. Knowing that this is such an obsolete way of doing things, I'm posting there so others like me that are forced to maintain old programs can benefit.

The template for doing a typed datatable is the following:

Imports System.Data
Imports System.ComponentModel
Imports System.Runtime.Serialization
Imports System.Diagnostics

'''<summary>
'''Represents the strongly named DataTable class.
'''</summary>
<Global.System.Serializable(), _
 Global.System.Xml.Serialization.XmlSchemaProviderAttribute("GetTypedTableSchema")> _
Partial Public Class tblMyTable
    Inherits TypedTableBase(Of tblMyTableRow)

    'Those are the StoredProcs names for (MANUAL) CRUD operations that the DBContext wrapper uses. (yuck! I hate thousands of them)
    'Public Const COMMAND_SAVE As String = "sp_MyTable_Save"
    'Public Const COMMAND_DELETE As String = "sp_MyTable_Delete"
    'Public Const COMMAND_LOADBY_ID As String = "sp_MyTable_LoadBy_Id"

    'Those are constants I maintain for untyped (but somewhat strong) compatibility
    Public Const FIELD_pID As String = "pID"
    Public Const FIELD_SomeOther As String = "SomeOtherField"

    'Basic CRUD, uses company data as the app hot swapps DBs (one for company)
    'Public Function Save(ByVal company As DataRow) As Short
    '    Return New Base(company).Update(Me, COMMAND_SAVE, COMMAND_DELETE)
    'End Function

    'Public Sub LoadByID(ByVal company As DataRow, Id As Integer)
    '    Me.Rows.Clear()
    '    Me.Merge(New Base(company).FillDataTable(Of tblMyTable)(COMMAND_LOADBY_ID, Id))
    'End Sub

    <DebuggerNonUserCodeAttribute()>
    Private Sub InitClass()
        Me.columnpID = New DataColumn(FIELD_pID, GetType(Integer), Nothing, MappingType.Element) With
                   {.AllowDBNull = False, .ReadOnly = True, .Unique = True,
                    .AutoIncrement = True, .AutoIncrementSeed = -1, .AutoIncrementStep = -1}
        MyBase.Columns.Add(Me.columnpID)
        Me.columnSomeOtherField = New DataColumn(FIELD_SomeOther, GetType(String), Nothing, MappingType.Element) With
                           {.MaxLength = 5, .AllowDBNull = False, .DefaultValue = String.Empty}
        MyBase.Columns.Add(Me.columnSomeOtherField)
    End Sub

    Private columnpID As DataColumn
    Private columnSomeOtherField As DataColumn

    <DebuggerNonUserCodeAttribute()>
    Public Sub New()
        MyBase.New()
        Me.TableName = "tblMyTable"
        Me.BeginInit()
        Me.InitClass()
        Me.EndInit()
    End Sub

    <DebuggerNonUserCodeAttribute()>
    Friend Sub New(ByVal table As DataTable)
        MyBase.New()
        Me.TableName = table.TableName
        If (table.CaseSensitive <> table.DataSet.CaseSensitive) Then
            Me.CaseSensitive = table.CaseSensitive
        End If
        If (table.Locale.ToString <> table.DataSet.Locale.ToString) Then
            Me.Locale = table.Locale
        End If
        If (table.Namespace <> table.DataSet.Namespace) Then
            Me.Namespace = table.Namespace
        End If
        Me.Prefix = table.Prefix
        Me.MinimumCapacity = table.MinimumCapacity
    End Sub

    <DebuggerNonUserCodeAttribute()>
    Protected Sub New(ByVal info As Global.System.Runtime.Serialization.SerializationInfo, ByVal context As Global.System.Runtime.Serialization.StreamingContext)
        MyBase.New(info, context)
        Me.InitVars()
    End Sub

    <DebuggerNonUserCodeAttribute()>
    Public ReadOnly Property pIDColumn() As DataColumn
        Get
            Return Me.columnpID
        End Get
    End Property

    <DebuggerNonUserCodeAttribute()>
    Public ReadOnly Property SomeOtherFieldColumn() As DataColumn
        Get
            Return Me.columnSomeOtherField
        End Get
    End Property

    <DebuggerNonUserCodeAttribute(), Browsable(False)>
    Public ReadOnly Property Count() As Integer
        Get
            Return Me.Rows.Count
        End Get
    End Property

    <DebuggerNonUserCodeAttribute()>
    Default Public ReadOnly Property Item(ByVal index As Integer) As tblMyTableRow
        Get
            Return CType(Me.Rows(index), tblMyTableRow)
        End Get
    End Property

    <DebuggerNonUserCodeAttribute()>
    Public Overrides Function Clone() As DataTable
        Dim cln As tblMyTable = CType(MyBase.Clone, tblMyTable)
        cln.InitVars()
        Return cln
    End Function

    <DebuggerNonUserCodeAttribute()>
    Protected Overrides Function CreateInstance() As DataTable
        Return New tblMyTable()
    End Function

    <DebuggerNonUserCodeAttribute()>
    Friend Sub InitVars()
        Me.columnpID = MyBase.Columns(FIELD_pID)
        Me.columnSomeOtherField = MyBase.Columns(FIELD_SomeOther)
    End Sub

    <DebuggerNonUserCodeAttribute()>
    Public Function NewtblMyTableRow() As tblMyTableRow
        Return CType(Me.NewRow, tblMyTableRow)
    End Function

    <DebuggerNonUserCodeAttribute()>
    Protected Overrides Function NewRowFromBuilder(ByVal builder As DataRowBuilder) As DataRow
        Return New tblMyTableRow(builder)
    End Function

    <DebuggerNonUserCodeAttribute()>
    Protected Overrides Function GetRowType() As Global.System.Type
        Return GetType(tblMyTableRow)
    End Function

    <DebuggerNonUserCodeAttribute()>
    Public Sub RemovetblMyTableRow(ByVal row As tblMyTableRow)
        Me.Rows.Remove(row)
    End Sub

End Class

'''<summary>
'''Represents strongly named DataRow class.
'''</summary>
Partial Public Class tblMyTableRow
    Inherits DataRow

    Private tabletblMyTable As tblMyTable

    <DebuggerNonUserCodeAttribute()>
    Friend Sub New(ByVal rb As DataRowBuilder)
        MyBase.New(rb)
        Me.tabletblMyTable = CType(Me.Table, tblMyTable)
    End Sub

    <DebuggerNonUserCodeAttribute()>
    Public Property pID() As Integer
        Get
            Return CType(Me(Me.tabletblMyTable.pIDColumn), Integer)
        End Get
        Set(value As Integer)
            Me(Me.tabletblMyTable.pIDColumn) = value
        End Set
    End Property

    <DebuggerNonUserCodeAttribute()>
    Public Property SomeOtherField() As String
        Get
            Return CType(Me(Me.tabletblMyTable.SomeOtherFieldColumn), String)
        End Get
        Set(value As String)
            Me(Me.tabletblMyTable.SomeOtherFieldColumn) = value
        End Set
    End Property
End Class

That's all that you need. Perhaps It could be reduced, but then the dataset functions would not work as expected.

If you want that code, generated automagically for you by the ID (VS2010) you must follow those steps:

  1. On server explorer, create the connection to your favorite DB
  2. Right-click on top of your project and select adding a new element.
  3. Just pick the dataset object template, the name is irrelevant. It will open in designer view.
  4. Pick the table from the database and drag to the dataset designer.
  5. Then... look at the class selector on top.
  6. Unfold and locate [yourTableName]Datatable. Click on it.
  7. It will jump to the said class in the DataSet1.designer.vb (cs) file.
  8. The next class it's the row definition. Just copy-paste them into a new class file.
  9. If you want a more complete datatable object, the next class below the row class define events, and the delegate it's just above the table def.

Simple and I tested it to work in conjunction of the remaining program, that uses untyped. Perhaps it would be like polishing a turd, but I would like to add data annotations somewhere to do some client validations like in EF. And perhaps replace the columns constructor parameters for them. (but I caaan't)

good Luck.

OTHER TIPS

Not sure I'm following completely but it looks like you're defining FIELD_SomeField2 as a double

(This Line in first snippet)

.Add(FIELD_SomeField2, GetType(System.Double)).DefaultValue = 0

But then I see you're defining SomeField2 as a DateTime in your second snippet.

<Required()>
<DataType(DataType.DateTime)>
Friend Property SomeField2 As DataColumn

So maybe just a type mismatch...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top