Overblog
Suivre ce blog Administration + Créer mon blog
16 mars 2013 6 16 /03 /mars /2013 16:33

Il est important de protéger son code. Même si Dotfuscator CE n’a pas toutes les fonctionnalités nécessaires à une bonne obfuscation il permet néanmoins de rendre le code moins compréhensible.

Dotfuscator CE ne permet pas d’obfusquer le code à la volé et de le signer lorsque l’on utilise le bouton “Publish Wizard” de Visual Studio 2012 pour un projet ClickOnce.

Il est cependant possible de le faire en suivant les étapes suivantes (Configuration Release):

 

1- Faite un Rebuild de votre application

2- Copiez tous les .dll se trouvant dans le dossier /bin/Release dans le dossier /obj/Release

3- Ouvrez DotFuscator CE et obfusquez votre application (.exe) se trouvant dans le dossier /obj/Release

4- Remplacez le fichier de votre application se trouvant dans /obj/Release par celui généré par DotFuscator CE se trouvant généralement dans le dossier /Mes documents/DotFuscated

5- Dans les propriétés de votre projet, dans l’onglet “Publish”, cliquez sur “Publish Now

 

Le bouton “Publish Now” considére que le build de votre application est à jour et va donc créer le package de déploiement à partir de votre fichier obfusqué !

Partager cet article
Repost0
27 septembre 2011 2 27 /09 /septembre /2011 15:46

Considérons le code suivant représentant une énumération des jours de la semaine :

 

 Public Enum Days 
 Sunday 
 Monday 
 Tuesday 
 Wednesday 
 Thursday 
 Friday 
 Saturday 
 End Enum 

 

Avec cette énumération on peut facilement changer la valeur d’une variable en faisant ceci :

 

 Dim myDay as Integer = Days.Sunday 

 

Mais qu’en est-il si nous devions spécifier plusieurs jours ou tous les jours. Pour ce faire nous devons rendre notre énumération Bit-Wise en y ajoutant l’attribut <Flags()> et en donnant une valeur à la puissance 2 à tous les éléments :

 

 

 <Flags()> _ 
 Public Enum Days 
 Sunday = 1 
 Monday = 2 
 Tuesday = 4 
 Wednesday = 8 
 Thursday = 16 
 Friday = 32 
 Saturday = 64 
 All = Sunday Or Monday Or Tuesday Or Wednesday Or Thursday Or Friday Or Saturday 
 End Enum 

Remarquez que j’ai ajouté “All” permettant de spécifier tous les jours.

Avec cette énumération il est désormais possible de faire :

 

 Dim myDays as Days 
 myDays = myDays Or Sunday Or Friday ' Ajoute le Dimanche et le Vendredi 
 myDays = myDays And Not Saturday ' Enlève le Samedi 

 

Pour savoir si le Dimanche a été choisi il faut faire ceci :

 

 If (myDays And Days.Sunday) = Days.Sunday Then 
 ' mon code du Dimanche 
 End If 
Partager cet article
Repost0
22 juin 2011 3 22 /06 /juin /2011 07:53

Il existe plusieurs méthodes pour vérifier les droits d’accès en écriture d’un dossier. Mais ces méthodes ne fonctionnent pas toujours sur tous les types de lecteurs comme les lecteurs réseaux par exemple.

La méthode la plus sur pour tester les droits d’accès est de tout simplement essayer d’écrire un fichier dans le dossier et de gérer l’erreur UnauthorizedAccessException.

 

Voici un exemple de cette méthode :

 

 Public Function IsFolderHasWriteAccess(path As String) As Boolean 
 
 Try 
 Dim filePath As String = path & "writeTest.tmp" 
 Dim fs As FileStream = New FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write) 
 fs.Close() 
 
 If My.Computer.FileSystem.FileExists(filePath) Then 
 My.Computer.FileSystem.DeleteFile(filePath, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.DeletePermanently) 
 End If 
 Return True 
 Catch ex As UnauthorizedAccessException 
 Return False 
 Catch ex As Exception 
 Return False 
 End Try 
 
 Return False 
 
 End Function 
Partager cet article
Repost0
31 mai 2011 2 31 /05 /mai /2011 18:27

Cette erreur provient souvent du faite que les ressources n’ont pas été libéré ou que les fichiers Metadata se trouvent dans les mêmes dossiers que les dossiers synchronisés.

 

Premièrement il ne faut pas oublié de faire un Dispose sur le FileSyncProvider une fois la synchronisation terminée. Ensuite il faut ajouter les fichiers Metadata à vos fichiers exclus.

 

Voici un exemple :

 Private Sub DetectChangesOnFileSystemReplica(ByVal replicaRootPath As String, ByVal filter As FileSyncScopeFilter, _ 
 ByVal options As FileSyncOptions) 
 
 
 Dim provider As FileSyncProvider = Nothing 
 
 Try 
 provider = New FileSyncProvider(replicaRootPath, filter, options) 
 
 ' J'ajoute les fichiers metadata à la liste des fichiers exclus 
 filter.FileNameExcludes.Add(provider.MetadataFileName) 
 filter.FileNameExcludes.Add(provider.MetadataFileName) 
 
 provider.DetectChanges() 
 
 Finally 
 ' Je libère les ressources 
 If provider IsNot Nothing Then 
 provider.Dispose() 
 End If 
 
 End Try 
 End Sub 
Partager cet article
Repost0
1 septembre 2010 3 01 /09 /septembre /2010 09:59

 

Les fichiers dll du global assembly cache se trouve dans le répertoire c:\windows\assembly. Le problème c’est que les dll sont cachés dans des sous dossiers.

 

Voici comment procéder pour les extraires. Nous allons prendre comme exemple le fichier Microsoft.Sqlserver.Diagnostics.STrace.dll.

 

1- Exécutez en tant qu’administrateur l’invite de commandes (Tous les programmes –>Accessoires)

2- Tapez cd c:\windows\assembly\GAC_MSIL\Microsoft.Sqlserver.Diagnostics.STrace (ou le nom du dll que vous recherchez)

3- Tapez dir pour obtenir les sous répertoires

4- Vous devriez voir un sous répertoire dont le nom représente la version du fichier dll. Dans mon cas le dossier porte le nom de 10.0.0.0__89845dcd8080cc91

5- Tapez cd 10.0.0.0__89845dcd8080cc91

6- Pour extraire le fichier dll et le mettre à la racine du lecteur C: tapez : copy Microsoft.Sqlserver.Diagnostics.STrace.dll c:\

Partager cet article
Repost0
13 août 2010 5 13 /08 /août /2010 10:05

Avant de manipuler les données dans votre application vous devez vous assurer que le service de base de données SQL Server est bien installé sur l’ordinateur et qu’il est démarré.

 

Le code suivant permet de réaliser cette vérification :

 

 

 Public NotInheritable Class SQLServer 
 
 Public Sub New() 
 End Sub 
 
 Public Shared Sub StartService() 
 
On vérifie que le service existe 
 If IsServiceExist() Then 
 Dim controler As New ServiceController 
 
 controler.MachineName = "." 
 controler.ServiceName = "MSSQL$SQLEXPRESS" 
 
 Try 
 Select Case controler.Status 
 
 Case ServiceControllerStatus.Running 
 ‘ le service est démarré nous n’avons rien à faire 
 
 Case ServiceControllerStatus.Stopped 
 
 ‘ le service est arrêté nous allons tenter de le redémarrer 
 controler.Start() 
 controler.WaitForStatus(ServiceControllerStatus.Running) 
 Case ServiceControllerStatus.Paused 
 
 ‘ le service est en pause nous allons tenter de le relancer 
 controler.Continue() 
 controler.WaitForStatus(ServiceControllerStatus.Running) 
 End Select 
 Catch ex As Exception 
 
 ‘ en cas d’échec du démarrage du service on ferme l’application 
 MsgBox("Une erreur c'est produite lors du démarrage du service de base de données SQL Server (SQLEXPRESS). Vérifiez que SQL Server 2008 Express est bien installé sur votre ordinateur et que le service SQL Server (SQLEXPRESS) est bien démarré. L'application va se fermer.", MsgBoxStyle.Critical) 
 My.Application.Shutdown() 
 End Try 
 Else 
 
 ‘ si le service n’existe pas on ferme l’application 
 MsgBox("Le service de base de données SQL Server (SQLEXPRESS) n'est pas installé sur votre ordinateur. Veuillez réinstaller l'application.", MsgBoxStyle.Critical) 
 
 My.Application.Shutdown() 
 End If 
 End Sub 
 
 
 
 ‘ cette fonction retourne True si le service SQL Server (SQLEXPRESS) existe 
 
 Public Shared Function IsServiceExist() As Boolean 
 For Each service As ServiceController In ServiceController.GetServices 
 If service.ServiceName = "MSSQL$SQLEXPRESS" Then 
 Return True 
 End If 
 Next 
 Return False 
 End Function 
 
 
 End Class 
 

 

 

Vous n’avez plus qu’à appeler SQLServer.StartService au démarrage de votre application.

Partager cet article
Repost0
15 juillet 2010 4 15 /07 /juillet /2010 09:48

Certains contrôles de votre application peuvent ne fonctionner que sous Windows 7 ou Vista et pas sous Windows XP.

C’est le cas par exemple si vous utilisez le WindowsAPICodePack. Il est donc nécessaire de savoir sous quel Système d’exploitation est installé votre application pour activer ou désactiver certains contrôles.

 

 

Nous allons, pour se faire, créer un module avec une fonction GetOsName :

 

 <HideModuleName()> _ 
 Module OsName 
 
 
 Public Enum OsNames 
 Windows95 
 Windows98 
 WindowsME 
 WindowsNT3 
 WindowsNT4 
 Windows2000 
 WindowsXP 
 Windows2003 
 WindowsVista 
 Windows7 
 Unknown 
 End Enum 
 
 
 
 Public Function GetOSName() As OsNames 
 Dim os As System.OperatingSystem = System.Environment.OSVersion 
 Dim osName As String = OsNames.Unknown 
 Select Case os.Platform 
 Case System.PlatformID.Win32Windows 
 Select Case os.Version.Minor 
 Case 0 
 osName = OsNames.Windows95 
 Exit Select 
 Case 10 
 osName = OsNames.Windows98 
 Exit Select 
 Case 90 
 osName = OsNames.WindowsME 
 Exit Select 
 End Select 
 Exit Select 
 Case System.PlatformID.Win32NT 
 Select Case os.Version.Major 
 Case 3 
 osName = OsNames.WindowsNT3 
 Exit Select 
 Case 4 
 osName = OsNames.WindowsNT4 
 Exit Select 
 Case 5 
 If os.Version.Minor = 0 Then 
 osName = OsNames.Windows2000 
 ElseIf os.Version.Minor = 1 Then 
 osName = OsNames.WindowsXP 
 ElseIf os.Version.Minor = 2 Then 
 osName = OsNames.Windows2003 
 End If 
 Exit Select 
 Case 6 
 osName = OsNames.WindowsVista 
 If os.Version.Minor = 0 Then 
 osName = OsNames.WindowsVista 
 ElseIf os.Version.Minor = 1 Then 
 osName = OsNames.Windows7 
 End If 
 Exit Select 
 End Select 
 Exit Select 
 End Select 
 Return osName 
 End Function 
 
 End Module 
 
 

 

Voici comment utiliser cette fonction :

 

 Select Case GetOSName() 
 Case OsNames.Windows7, OsNames.WindowsVista 
 ‘ j’active ici mes contrôles pour Windows 7 et Vista 
 
 Case OsNames.WindowsXP 
 ‘ Je désactive ici mes contrôles pour Windows 7 et j’active ceux pour Windows XP 
 
 End Select 
 
Partager cet article
Repost0
6 juillet 2010 2 06 /07 /juillet /2010 15:08

 

Si vous tentez d’accéder aux informations d’un DataRow dont le statut est “Deleted” vous obtiendrez un message d’erreur vous indiquant que vous ne pouvez pas y accéder.

 

Voici comment procéder pour accéder un DataRow Deleted :

 

Code :

Private Sub Clients_RowDeleted(ByVal sender As Object, ByVal e As DataRowChangeEventArgs)
            Dim rowClient As ClientsRow = e.Row

            MsgBox(rowClient("nomDuClient", DataRowVersion.Original))
End Sub

Cet exemple affiche dans une boîte de dialogue le nom du client qui vient d’être effacé. Nous utilisons le type ClientsRow de la Table Clients d’un Dataset fortement typé.

Partager cet article
Repost0
28 avril 2010 3 28 /04 /avril /2010 07:47

Mon objectif est le suivant : Sauvegarder dans un même fichier XML les données d’un Dataset et les préférences de l’utilisateur pour faire une sauvegarde globale de mon application.

Un dataset est par nature “Serializable” mais il n’en est pas de même pour l’objet My.Settings dont le niveau de protection est trop élevé pour être sérialisé.

J’ai donc décidé de faire une classe personnalisée “Serializable” pour stocker les préférences de l’utilisateur et une extension “My.Preferences”

 

Pour sérialiser et désérialiser des objets j’ai créé une classe dans l’espace de nom HC.Xml dont voici le code :

Code:

Imports System.Xml
Imports System.IO
Imports System.Xml.Serialization

Public Class Serialization

    Private _obj As Object

    Public Sub New(ByVal obj As Object)
        _obj = obj
    End Sub

    Public Sub WriteToXmlFile(ByVal filePath As String)
        Dim stream As StreamWriter = New StreamWriter(filePath)
        Dim serializer As XmlSerializer = New XmlSerializer(_obj.GetType)
        serializer.Serialize(stream, _obj)
        stream.Close()
    End Sub

    Public Function ConvertXmlFileToObject(ByVal filePath As String)
        Dim stream As StreamReader = New StreamReader(filePath)
        Dim serializer As XmlSerializer = New XmlSerializer(_obj.GetType)
        _obj = serializer.Deserialize(stream)
        stream.Close()
        Return _obj
    End Function

End Class

 

J’ai ensuite créé ma classe personnalisé “customSettings” qui est dans l’espace de nom Version1 de mon application :

 

Code :

Imports System.Xml
Imports System.Xml.Serialization

Namespace Version1
    <Serializable(), XmlRoot()> _
    Public Class customSettings

        Public Sub New()
            Me.Load()
        End Sub

        Public Sub Save()
            Dim xmlFile As New HC.Xml.Serialization(Me)
            xmlFile.WriteToXmlFile(My.Application.Info.DirectoryPath & "preferences.xml")
        End Sub

        Public Sub Load()
            Dim filePath As String = My.Application.Info.DirectoryPath & "preferences.xml"
            If My.Computer.FileSystem.FileExists(filePath) Then
                Dim xmlFile As New HC.Xml.Serialization(Me)
                xmlFile.ConvertXmlFileToObject(filePath)
            End If
        End Sub

        Private chargesSurVenteValue As Decimal = 13
        <XmlElement()> _
        Public Property ChargeSurVente() As Decimal
            Get
                Return chargesSurVenteValue
            End Get
            Set(ByVal value As Decimal)
                chargesSurVenteValue = value
            End Set
        End Property

        Private chargesSurServiceValue As Decimal = 23
        <XmlElement()> _
        Public Property ChargesSurService() As Decimal
            Get
                Return chargesSurServiceValue
            End Get
            Set(ByVal value As Decimal)
                chargesSurServiceValue = value
            End Set
        End Property

    End Class
End Namespace

 

 

Je dois maintenant ajouter cette classe à l’espace de nom My :

 

Code :

Namespace My
    <HideModuleName()> _
    Module customPreferences
        Private _pref As New ThreadSafeObjectProvider(Of customSettings)
        Public ReadOnly Property Preferences As customSettings
            Get
                Return _pref.GetInstance
            End Get
        End Property
    End Module
End Namespace

Remarquez que si “HideModuleName” n’était pas spécifié, nous aurions My.customPreferences.Preferences au lieu de My.Preferences. 

 

Voilà, je peux désormais utiliser My.Preferences pour stocker les préférences de mon utilisateur. Lorsque je utilise la méthode My.Preferences.Save, l’objet est sérialisé et sauvegarder dans le fichier xml “preferences.xml” dans le répertoire de mon application. La méthode Load permet de désérialiser l’objet pour récupérer les données et est appelé dans le constructeur.

 

Pour pouvoir faire ma sauvegarde globale, il ne me reste plus qu’a créer une classe “serializable” contenant mon dataset et mes préférences.

 

Code :

Public Class Backup

        Private ds As New DataSetAGPV1

        Public Sub BackupDatabaseAndSettings(Optional ByVal fileName As String = Nothing)
            Me.FillDataset()

            Dim bkf As New BackupFile
            bkf.DataSet = ds
            bkf.Preferences = My.Preferences

            Dim XmlSerial As New HC.Xml.Serialization(bkf)

            If fileName IsNot Nothing Then
                XmlSerial.WriteToXmlFile(fileName)
             End If
        End Sub

        Private Sub FillDataset()
            For Each dt As DataTable In ds.Tables
                FillTable(dt)
            Next

        End Sub

        Private Sub FillTable(ByVal dt As DataTable)
            Using con As New SqlConnection(My.Settings.dbAGPV1ConnectionString)
                Using da As New SqlDataAdapter("SELECT * FROM " & dt.TableName, con)
                    da.Fill(ds, dt.TableName)
                End Using
            End Using
        End Sub

        <Serializable(), XmlRoot()> _
        Public Class BackupFile

            Private _version As String

            Public Sub New()
                _version = My.Application.Info.Version.ToString
            End Sub

            <XmlAttribute()> _
            Public Property Version As String
                Get
                    Return _version
                End Get
                Set(ByVal value As String)
                    _version = value
                End Set
            End Property

            Private ds As DataSetAGPV1
            <XmlElement()> _
            Public Property DataSet() As DataSetAGPV1
                Get
                    Return ds
                End Get
                Set(ByVal value As DataSetAGPV1)
                    ds = value
                End Set
            End Property

            Private preferencesValue As Version1.customSettings
            <XmlElement()> _
            Public Property Preferences() As Version1.customSettings
                Get
                    Return preferencesValue
                End Get
                Set(ByVal value As Version1.customSettings)
                    preferencesValue = value
                End Set
            End Property

        End Class

    End Class

 

La classe Backup charge les données dans le dataset avant de le mettre dans la classe “serializable” BackupFile avec les préférences. Ensuite la classe Backup file est sérialisé et sauvegardée sous la forme d’un fichier XML. J’ai ainsi sauvegarder les données de mon application et les préférences de l’utilisateur dans un même fichier.

La classe BackupFile contient également une propriété “Version” qui permet, lors de la restauration de données, de vérifier si la version de l’application et de la sauvegarde sont les même.

Partager cet article
Repost0
9 avril 2010 5 09 /04 /avril /2010 18:33

Le développeurs ont pris l’habitude de manipuler les données à l’aide de Dataset et de DataTable. Depuis l’arrivé  de LINQ leurs habitudes ont un peu changé et ils ont du s’habituer à travailler plus souvent avec des IEnumerable(of Anonyme).

La méthode CopyToDatatable permet de copier les résultats d’une requête LINQ dans un DataTable.

 

Voici un exemple :

 

Code :

Dim dt As New DataTable

Dim resultat = From element As DataRow in maDataTable Where element.monChamp=maVariable Select element

resultat.CopyToDataTable(dt)

Cette méthode est très pratique si l’on veut continuer de travailler avec des DataTables. Le problème c’est que cette méthode n’est disponible que si le résultat de votre requête LINQ est de type DataRow.

 

Pour pouvoir utiliser CopyToDataTable pour d’autres types, vous devez ajouter le code suivant à votre application.

 

Créez une classe ObjectShredder(Of T) (ce code provient de MSDN) :

 

Code :

Imports System.Reflection

Public Class ObjectShredder(Of T)

    Private _fi As FieldInfo()
    Private _ordinalMap As Dictionary(Of String, Integer)
    Private _pi As PropertyInfo()
    Private _type As Type

  
    Public Sub New()
        Me._type = GetType(T)
        Me._fi = Me._type.GetFields
        Me._pi = Me._type.GetProperties
        Me._ordinalMap = New Dictionary(Of String, Integer)
    End Sub

    Public Function ShredObject(ByVal table As DataTable, ByVal instance As T) As Object()
        Dim fi As FieldInfo() = Me._fi
        Dim pi As PropertyInfo() = Me._pi
        If (Not instance.GetType Is GetType(T)) Then
            ' Si l'instance dérive de T, étendre le schéma de la table
            ' et récupérer les propirétés et les champs
            Me.ExtendTable(table, instance.GetType)
            fi = instance.GetType.GetFields
            pi = instance.GetType.GetProperties
        End If

        ' Add the property and field values of the instance to an array.
        Dim values As Object() = New Object(table.Columns.Count - 1) {}
        Dim f As FieldInfo
        For Each f In fi
            values(Me._ordinalMap.Item(f.Name)) = f.GetValue(instance)
        Next
        Dim p As PropertyInfo
        For Each p In pi
            values(Me._ordinalMap.Item(p.Name)) = p.GetValue(instance, Nothing)
        Next

        ' Return the property and field values of the instance.
        Return values
    End Function

    ' Summary:           Loads a DataTable from a sequence of objects.
    ' source parameter:  The sequence of objects to load into the DataTable.</param>
    ' table parameter:   The input table. The schema of the table must match that
    '                    the type T.  If the table is null, a new table is created 
    '                    with a schema created from the public properties and fields
    '                    of the type T.
    ' options parameter: Specifies how values from the source sequence will be applied to
    '                    existing rows in the table.
    ' Returns:           A DataTable created from the source sequence.

    Public Function Shred(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As LoadOption?) As DataTable

        ' Load the table from the scalar sequence if T is a primitive type.
        If GetType(T).IsPrimitive Then
            Return Me.ShredPrimitive(source, table, options)
        End If

        ' Create a new table if the input table is null.
        If (table Is Nothing) Then
            table = New DataTable(GetType(T).Name)
        End If

        ' Initialize the ordinal map and extend the table schema based on type T.
        table = Me.ExtendTable(table, GetType(T))

        ' Enumerate the source sequence and load the object values into rows.
        table.BeginLoadData()
        Using e As IEnumerator(Of T) = source.GetEnumerator
            Do While e.MoveNext
                If options.HasValue Then
                    table.LoadDataRow(Me.ShredObject(table, e.Current), options.Value)
                Else
                    table.LoadDataRow(Me.ShredObject(table, e.Current), True)
                End If
            Loop
        End Using
        table.EndLoadData()

        ' Return the table.
        Return table
    End Function

    Public Function ShredPrimitive(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As LoadOption?) As DataTable
        ' Create a new table if the input table is null.
        If (table Is Nothing) Then
            table = New DataTable(GetType(T).Name)
        End If
        If Not table.Columns.Contains("Value") Then
            table.Columns.Add("Value", GetType(T))
        End If

        ' Enumerate the source sequence and load the scalar values into rows.
        table.BeginLoadData()
        Using e As IEnumerator(Of T) = source.GetEnumerator
            Dim values As Object() = New Object(table.Columns.Count - 1) {}
            Do While e.MoveNext
                values(table.Columns.Item("Value").Ordinal) = e.Current
                If options.HasValue Then
                    table.LoadDataRow(values, options.Value)
                Else
                    table.LoadDataRow(values, True)
                End If
            Loop
        End Using
        table.EndLoadData()

        ' Return the table.
        Return table
    End Function

    Public Function ExtendTable(ByVal table As DataTable, ByVal type As Type) As DataTable
        ' Extend the table schema if the input table was null or if the value
        ' in the sequence is derived from type T.
        Dim f As FieldInfo
        Dim p As PropertyInfo

        For Each f In type.GetFields
            If Not Me._ordinalMap.ContainsKey(f.Name) Then
                Dim dc As DataColumn

                ' Add the field as a column in the table if it doesn't exist
                ' already.
                dc = IIf(table.Columns.Contains(f.Name), table.Columns.Item(f.Name), table.Columns.Add(f.Name, f.FieldType))

                ' Add the field to the ordinal map.
                Me._ordinalMap.Add(f.Name, dc.Ordinal)
            End If

        Next

        For Each p In type.GetProperties
            If Not Me._ordinalMap.ContainsKey(p.Name) Then
                ' Add the property as a column in the table if it doesn't exist
                ' already.
                Dim dc As DataColumn
                dc = IIf(table.Columns.Contains(p.Name), table.Columns.Item(p.Name), table.Columns.Add(p.Name, p.PropertyType))

                ' Add the property to the ordinal map.
                Me._ordinalMap.Add(p.Name, dc.Ordinal)
            End If
        Next

        ' Return the table.
        Return table
    End Function

End Class

 

 

Ensuite, créez un module :

 

Code :

Imports System.Runtime.CompilerServices

Public Module CustomLINQtoDataSetMethods
    <Extension()> _
    Public Function CopyToDataTable(Of T)(ByVal source As IEnumerable(Of T)) As DataTable
        Return New ObjectShredder(Of T)().Shred(source, Nothing, Nothing)
    End Function

    <Extension()> _
    Public Function CopyToDataTable(Of T)(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As LoadOption?) As DataTable
        Return New ObjectShredder(Of T)().Shred(source, table, options)
    End Function

End Module

 

Vous pouvez désormais utiliser CopyToDataTable avec d’autres Types !

Partager cet article
Repost0