2017-10-16 72 views
0

我遇到的問題是在下面的代碼。該函數適用於ControlCaption實體類,但我必須將其複製並粘貼到其他需要讀取的其他實體類,然後將ControlCaption更改爲新類名(我不需要重複代碼)。因爲還有一個更新/插入值的函數,我嘗試將公共對象設置爲與該類相同(即Public SomeClass爲Object = New ControlCaption),但IDE對此抱怨。VB.NET - 從函數返回動態類值

下面是代碼:

Public Function SelectList(Optional ByVal SQLString As String = "SELECT * FROM " & DatabaseName) As List(Of ControlCaption) 
    Dim strConnectionString As String = ConnectionString()  '--Creates the connection string 
    Dim intLineNumber As Integer = 0 
    Dim InfoList As New List(Of ControlCaption) '--List of classes being returned 

    Try 
     If DatabaseType = SQLServer Then '--User indicated previously that a SQL Server was being used 
      Using myConnection As New SqlClient.SqlConnection(strConnectionString) 
       myConnection.Open() 
       Using myCommand As New SqlClient.SqlCommand(SQLString, myConnection) 
        Using myReader = myCommand.ExecuteReader 
         Do While myReader.Read 
          InfoList.Add(New ControlCaption()) '--Add a new element to the list 

          '--The next couple of lines need System.Reflection to be imported to work 
          '--The following for statement goes through each property in a class and assigns 
          '--the database value with the same name to the property. (Required to use entity classes) 
          Dim TheObject As New ControlCaption 
          Dim TheType As Type = TheObject.GetType() 
          Dim Properties() As PropertyInfo = TheType.GetProperties() 
          For Each Prop As PropertyInfo In properties 
           Try 
            If UCase(Prop.Name) <> "ITEM" Then 
             If TypeOf (myReader.Item(Prop.Name)) Is DateTime Then 
              '--Convert value to date 
              InfoList(InfoList.Count - 1).Item(Prop.Name) = CDate(IIf((myReader.Item(Prop.Name).ToString & String.Empty) = vbNullString, "1/1/1900", myReader.Item(Prop.Name).ToString)) 
             Else 
              InfoList(InfoList.Count - 1).Item(Prop.Name) = myReader.Item(Prop.Name).ToString & String.Empty 
             End If 
            End If 
           Catch ex As Exception 

           End Try 
          Next 
         Loop 
        End Using 
       End Using 

       If myConnection.State <> ConnectionState.Closed Then 
        myConnection.Close() 
       End If 

       Return InfoList 
      End Using 
     ElseIf DatabaseType = AccessDatabase Then 
      Using myConnection As New OleDb.OleDbConnection(strConnectionString) 
       myConnection.Open() 
       Using myCommand As New OleDb.OleDbCommand(SQLString, myConnection) 
        Using myReader = myCommand.ExecuteReader 
         Do While myReader.Read 
          InfoList.Add(New ControlCaption()) 

          Dim TheObject As New ControlCaption 
          Dim TheType As Type = TheObject.GetType() 
          Dim Properties() As PropertyInfo = TheType.GetProperties() 
          For Each Prop As PropertyInfo In properties 
           Try 
            If UCase(Prop.Name) <> "ITEM" Then 
             If TypeOf (myReader.Item(Prop.Name)) Is DateTime Then 
              InfoList(InfoList.Count - 1).Item(Prop.Name) = CDate(IIf((myReader.Item(Prop.Name).ToString & String.Empty) = vbNullString, "1/1/1900", myReader.Item("DateModified").ToString)) 
             Else 
              InfoList(InfoList.Count - 1).Item(Prop.Name) = myReader.Item(Prop.Name).ToString & String.Empty 
             End If 
            End If 
           Catch ex As Exception 

           End Try 
          Next 
         Loop 
        End Using 
       End Using 

       If myConnection.State <> ConnectionState.Closed Then 
        myConnection.Close() 
       End If 

       Return InfoList 
      End Using 
     Else 
      Return Nothing 
     End If 
    Catch ex As Exception 
     Return Nothing 
    End Try 
End Function 

我覺得有必須是使這個功能接受任何類的方式,但我還沒有想出一個辦法呢。

有沒有人有關於如何使這項工作的任何建議?任何改進建議也會有所幫助。

謝謝。


任何有興趣,這裏是已產生的最終代碼:

Public Function SelectList(Of EntityClass As New)(Optional ByVal SQLString As String = "SELECT * FROM " & DatabaseName) As List(Of EntityClass) 
    Try 
     Dim Entities As New List(Of EntityClass)() 
     Using Connection As New SqlClient.SqlConnection(ConnectionString) 
      Using Command As New SqlClient.SqlCommand(SQLString, Connection) 
       Connection.Open() 
       Using Reader = Command.ExecuteReader() 
        Dim Properties = GetType(EntityClass).GetProperties() 
        Do While Reader.Read 
         Dim Entity = CreateEntity(Of EntityClass)(Reader, Properties) 
         Entities.Add(Entity) 
        Loop 
       End Using 
      End Using 
     End Using 

     Return Entities 
    Catch ex As Exception 
     MsgBox(Err.Description) 
     Return Nothing 
    End Try 
End Function 

Private Function CreateEntity(Of PassedEntity As New)(ByVal reader As DbDataReader, ByVal properties As PropertyInfo()) As PassedEntity 
    Dim Entity As New PassedEntity() 
    For Each _Property As PropertyInfo In properties 
     If _Property.Name.ToUpper() = "ITEM" Then Continue For 

     Dim value = reader.Item(_Property.Name) 
     _Property.SetValue(Entity, value, Nothing) 
    Next 
End Function 
+0

使其具有通用性 – djv

+0

您確定編譯了方法中具有默認值的可選參數嗎?我擔心默認值應該是不變的值 – Fabio

+0

VB.NET中的類對我來說是相當新的。謝謝你的幫助。 –

回答

1

似乎很適合用「新」約束泛型。
「新」約束將有可能創建給定類型的新實例。

Public Function SelectList(Of T As New)(query As String) As List(Of T) 
    Dim list As New List(Of T)() 

    ' Somewhere in the rows loop 
     Dim item = New T() 
     ' Fill properties 
     list.Add(item) 

    Return list 
End Function 

如果你不喜歡重複的代碼,那麼你可以通過使用DbDataReader類,這是兩個SqlDataReaderOleDbDataReader一個基類中刪除一些重複。

Private Function CreateEntity<(Of T As New)(reader As DbDataReader, 
              properties As PropertyInfo()) As T 
    Dim entity As New T() 
    For Each property As PropertyInfo In properties 
     If property.Name.ToUpper() = "ITEM" Then Continue For 

     Dim value = reader.Item(property.Name) 
     ' Should work if property type is correspondent .NET type of sql column type 
     ' For null values property should be of Nullable type 
     property.SetValue(entity, value) 
     ' If not - use your logic 
    Next  
End Function 

在你的閱讀邏輯中使用這個函數。請注意,您不需要在每個循環中運行Type.GetProperties() - 讓它一次的類型和resue同一個集合

Public Function SelectList(Of T As New)(query As String) As List(Of T) 
    Dim entities As New List(Of T)() 

    Using connection As New SqlConnection(connectionString) 
     Using command As new SqlCommand(query, connection) 
      connection.Open() 
      Using reader = command.ExecuteReader() 
       Dim properties = GetType(T).GetProperties() 
       Do While reader.Read() 
        Dim entity = CreateEntity(reader, properties) 
        list.Add(entity) 
       Loop 
      End Using 
     ' No need for closing connection explicitly - End Using - will handle it 
     End Using 
    End Using 

    Return entities 
End Function 
+0

感謝您的幫助。我認爲我在VB6和VB.NET之間的轉換做得很好,但似乎有很多東西我錯過了,需要儘快學習。在你之間,法比奧和DJV的一些事情上,我能夠讓它工作。代碼大小的減少是驚人的。多謝你們。 'code' –

0

只需設置功能參數輸入Object,那麼你可以通過它的任何對象類型。例如:

Public Function SelectList(Optional ByVal SQLString As String = "SELECT * FROM " & DatabaseName) As List(Of Object) 
+0

感謝您的建議。這是我在尋求幫助之前嘗試過的一件事,但沒有奏效。 –

0

使它通用。只需返回可以實例化的對象列表。

它會被稱爲是這樣的:

Dim myFoo As List(Of Foo) = SelectList(Of Foo)("Select * FROM Foo") 

這是新代碼

Public Function SelectList(Of T As New)(Optional ByVal SQLString As String = "SELECT * FROM " & DatabaseName) As List(Of T) 
    Dim InfoList As New List(Of T) '--List of classes being returned 
    Dim connectionType = If(DatabaseType = SQLServer, connectionType.MsSql, connectionType.MsAccess) 
    Try 
     Using myConnection = getConnection(connectionType, ConnectionString()) 
      myConnection.Open() 
      Using myCommand = getCommand(connectionType, SQLString, myConnection) 
       Using myReader = myCommand.ExecuteReader 
        Do While myReader.Read 
         Dim TheObject As New T() 
         InfoList.Add(TheObject) '--Add a new element to the list 
         Dim TheType As Type = TheObject.GetType() 
         Dim Properties() As PropertyInfo = TheType.GetProperties() 
         For Each Prop As PropertyInfo In Properties 
          Try 
           If UCase(Prop.Name) <> "ITEM" Then 
            Prop.SetValue(TheObject, Convert.ChangeType(myReader.Item(Prop.Name), Prop.PropertyType), Nothing) 
           End If 
          Catch 
          End Try 
         Next 
        Loop 
       End Using 
      End Using 
      Return InfoList 
     End Using 
    Catch ex As Exception 
     Return Nothing 
    End Try 
End Function 

哦,等等,這裏也改變了其他的東西很多!我無法想象你複製函數的速度幾乎與複製查詢代碼的次數一樣多,所以我做了一個循環。使用這些函數來創建IDbConnectionIDbCommand對象,因爲該代碼的其餘部分是相同的(他們真的只是調用提供給那些接口功能反正)。

Private Function getConnection(connectionType As ConnectionType, connectionString As String) As IDbConnection 
    Select Case connectionType 
     Case ConnectionType.MsSql 
      Return New SqlClient.SqlConnection(connectionString) 
     Case ConnectionType.MsAccess 
      Return New OleDb.OleDbConnection(connectionString) 
     Case Else 
      Return Nothing 
    End Select 
End Function 

Private Function getCommand(connectionType As ConnectionType, commandString As String, connection As IDbConnection) As IDbCommand 
    Select Case connectionType 
     Case ConnectionType.MsSql 
      Return New SqlClient.SqlCommand() 
     Case ConnectionType.MsAccess 
      Return New OleDb.OleDbCommand() 
     Case Else 
      Return Nothing 
    End Select 
End Function 

Private Enum ConnectionType 
    MsSql 
    MsAccess 
End Enum 

(如果您曾經添加過另一種連接類型,只要它實現了這些接口就會很簡單)。

的屬性設置的代碼可以簡化,並假設日期是標準格式,只有一條線是需要設置的所有屬性

Prop.SetValue(TheObject, Convert.ChangeType(myReader.Item(Prop.Name), Prop.PropertyType), Nothing) 

此外,你可以看到,有沒有理由創建 ControlCaption對象有...

最後,你並不需要顯式地關閉連接,因爲你正確地有它在使用塊了。

+0

你是對的。感謝您的幫助。這工作也很好。 –