web-dev-qa-db-fra.com

Interroger la base de données Microsoft Access MDB à l'aide de LINQ et C #

J'ai un fichier de base de données * .MDB, et je me demande s'il est possible ou recommandé de le supprimer à l'aide de LINQ en C #. Je me demande également à quoi ressembleraient quelques exemples simples.

Je ne connais pas beaucoup LINQ, mais mes exigences pour cette tâche sont assez simples (je crois). L'utilisateur me transmettra un chemin d'accès à la base de données Microsoft Access MDB et j'aimerais utiliser LINQ pour ajouter des lignes à l'une des tables de la base de données.

36
Matthew Ruston

Ce que vous voulez, c'est un fournisseur LINQ to ODBC ou un fournisseur LINQ to JET/OLEDB.

En dehors de la boîte, MS n'en fabrique pas un. Il peut y avoir une 3ème partie qui fait.

14
FlySwat

En fait, j'ai récemment (aujourd'hui) découvert que vous pouvez accéder à une base de données Access avec LinqToSql. Il doit être au format 2002 ou supérieur, vous ne pourrez pas glisser-déposer les tables dans votre contexte de données. Vous devez donc créer manuellement les objets dans votre fichier dbml ou utiliser SQL Migration pour accès pour le déplacer vers un serveur SQL. puis faites glisser et déposez tout ce que vous voulez. Lorsque vous voulez réellement créer le contexte, transmettez-lui un OleDbConnection. Utilisez votre chaîne de connexion standard Jet.OLEDB.4.0 sur OleDbConnection et vous êtes prêt à partir. Pas sûr de la limitation que cela peut se produire cependant. Je viens de faire un échantillon rapide et un OrderBy sans problème.

13
David

J'ai écrit un petit exemple de programme pour tester cela avec la réponse de David. Vous devez créer une base de données d'accès et créer manuellement le DBML pour Linq-to-SQL, car vous ne pouvez pas les glisser-déposer.

Les insertions échouent, citant Missing semicolon (;) at end of SQL statement., mais les requêtes semblent fonctionner correctement.

Access database tables for Program

using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using Linq2Access.Data;

namespace Linq2Access
{
    class Program
    {
        static readonly string AppPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        static readonly string DbPath = Path.Combine(AppPath, "Data", "database.accdb");
        static readonly string DbConnString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + DbPath + "';Persist Security Info=False;";

        static void Main(string[] args)
        {
            if (!File.Exists(DbPath))
                throw new Exception("Database file does not exist!");

            using (OleDbConnection connection = new OleDbConnection(DbConnString))
            using (DataRepositoryDataContext db = new DataRepositoryDataContext(connection))
            {
                List<dbProject> projects = new List<dbProject>();
                for (int i = 1; i <= 10; i++)
                {
                    dbProject p = new dbProject() { Title = "Project #" + i };
                    for (int j = 1; j <= 10; j++)
                    {
                        dbTask t = new dbTask() { Title = "Task #" + (i * j) };
                        p.dbTasks.Add(t);
                    }
                    projects.Add(p);
                }

                try
                {
                    //This will fail to submit
                    db.dbProjects.InsertAllOnSubmit(projects);
                    db.SubmitChanges();
                    Console.WriteLine("Write succeeded! {0} projects, {1} tasks inserted",
                                        projects.Count,
                                        projects.Sum(x => x.dbTasks.Count));
                }
                catch(Exception ex)
                {
                    Console.WriteLine("Write FAILED. Details:");
                    Console.WriteLine(ex);
                    Console.WriteLine();
                }

                try
                {
                    //However, if you create the items manually in Access they seem to query fine
                    var projectsFromDb = db.dbProjects.Where(x => x.Title.Contains("#1"))
                                                        .OrderBy(x => x.ProjectID)
                                                        .ToList();

                    Console.WriteLine("Query succeeded! {0} Projects, {1} Tasks",
                                        projectsFromDb.Count,
                                        projectsFromDb.Sum(x => x.dbTasks.Count));
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Query FAILED. Details:");
                    Console.WriteLine(ex);
                    Console.WriteLine();
                }

                Console.WriteLine();
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
            }
        }
    }
}
7
jocull

Vous pouvez utiliser un DataSet. Il existe des extensions linq qui vous permettront d’interroger les données avec toutes les qualités de LINQ auxquelles nous sommes habitués :)

eICATDataSet.ICSWSbuDataTable tbl = new eICATDataSet.ICSWSbuDataTable();

ICSWSbuTableAdapter ta = new ICSWSbuTableAdapter();
ta.Fill(tbl);

var res = tbl.Select(x => x.ProcedureDate.Year == 2010);
1
Quinten Miller

LINQ to SQL ne fonctionne que pour les bases de données SQL Server. Ce dont vous avez besoin, c'est Microsoft Entity Framework. Cela rend l'accès orienté objet à votre mdb. À partir de cela, vous pouvez exécuter des requêtes LINQ.

http://msdn.Microsoft.com/en-us/library/aa697427(vs.80).aspx

0
GeekyMonkey

J'ai souvent vu cette question et dans plusieurs forums. J'ai essayé et voici une réponse complète pour ceux qui l'ont regardée.

LinQ n'a pas été conçu pour Access. Cependant, de nombreuses requêtes fonctionneront avec Access, y compris la procédure de suppression. Donc, selon moi, il n’ya que 2 lacunes cruciales dans le travail avec Access, à savoir:

  1. ne pas être capable de sauvegarder des données.
  2. ne pas être en mesure de faire glisser et déposer des objets sur le dbml

L'insertion échouera avec l'erreur "point-virgule manquant (;)". Cela est dû au fait que la procédure de sauvegarde LinQ a été créée pour sauvegarder les données et récupérer l’ID de clé primaire de l’enregistrement sauvegardé en une fois. Nous savons que vous ne pouvez pas exécuter plusieurs instructions SQL dans Access. C'est la raison de cet échec.

La mise à jour échouera avec l'erreur "enregistrement non trouvé". Une procédure de mise à jour recherchera la mise à jour de l'enregistrement, puis le mettra à jour. Je ne peux pas dire pourquoi il ne l'a pas trouvé, quand la requête normale de LinQ pour trouver un enregistrement fonctionne bien.

Étant donné que LinQ présente de nombreux avantages, j'ai découvert comment contourner le problème, tout en bénéficiant des autres avantages tout au long de mon application. Voici comment (NB: Mes codes sont dans VB.net, mais vous pouvez les convertir si nécessaire):

Créez la classe LinQ to SQL (.dbml) pour gérer votre LinQ par rapport à la base de données d'accès et un moyen de gérer votre procédure de sauvegarde. Vous trouverez ci-dessous les procédures complètes de ce que j'ai créé et je travaille maintenant avec LinQ to Access sans aucun problème:

Ajoutez une DataGridView sur un formulaire. Ajouter des boutons pour ajouter, modifier et supprimer

 enter image description here

Code pour remplir la grille:

Private Sub ResetForm()

    Try

        Using db As New AccessDataClassesDataContext(ACCCon)

            Dim rows = (From row In db.AccountTypes
                        Where row.AccountTypeID > 1
                        Order By row.AccountTypeID Ascending
                        Select row).ToList()
            Me.DataGridView1.DataSource = rows

        End Using

    Catch ex As Exception
        MessageBox.Show("Error: " & vbCr & ex.ToString, "Data Error", MessageBoxButtons.OK)
    End Try

End Sub

DetailForm

 enter image description here

Code pour définir les valeurs de contrôle

Private Sub ResetForm ()

    Try

        If _accountTypeID = 0 Then
            Exit Sub
        End If


        Using db As New AccessDataClassesDataContext(ACCCon)

            'Dim rows = (From row In db.AccountTypes
            '            Where row.AccountTypeID = _accountTypeID
            '            Order By row.AccountTypeID Ascending
            '            Select row.AccountTypeID, row.AccountType, row.LastUpdated).ToList()
            Dim rows = (From row In db.AccountTypes
                        Where row.AccountTypeID = _accountTypeID
                        Select row).ToList()

            For Each s In rows

                Me.AccountTypeIDTextBox.Text = s.AccountTypeID
                Me.myGuidTextBox.Text = s.myGuid
                Me.AccountTypeTextBox.Text = s.AccountType
                Me.AcHeadIDTextBox.Text = s.AcHeadID
                Me.DescriptionTextBox.Text = s.Description
                Me.LastUpdatedDateTimePicker.Value = s.LastUpdated

            Next

        End Using

    Catch ex As Exception

    End Try

End Sub

LinQToSQLClass

Vous devrez ajouter les objets de données au dbml manuellement, car vous ne pouvez pas faire de glisser-déposer lorsque vous utilisez Access. Notez également que vous devrez définir correctement toutes les propriétés des champs dans les fenêtres de propriétés. Plusieurs propriétés ne sont pas définies lorsque vous ajoutez les champs.

 enter image description here

Code à sauvegarder

Fonction publique SaveAccountType (type ByVal facultatif As String = "Fermer") As Boolean

    Dim success As Boolean = False
    Dim row As New AccountType

    Using db As New AccessDataClassesDataContext(ACCCon)

        If _accountTypeID > 0 Then

            row = (From r In db.AccountTypes
                   Where r.AccountTypeID = _accountTypeID).ToList()(0)

            If String.IsNullOrEmpty(row.AccountTypeID) Then
                MessageBox.Show("Requested record not found", "Update Customer Error")
                Return success
            End If

        End If

        Try

            With row
                .myGuid = Me.myGuidTextBox.Text
                .AccountType = Me.AccountTypeTextBox.Text
                .Description = Me.DescriptionTextBox.Text
                .AcHeadID = Me.AcHeadIDTextBox.Text
                .LastUpdated = Date.Parse(Date.Now())
            End With


            If _accountTypeID = 0 Then db.AccountTypes.InsertOnSubmit(row)
            db.SubmitChanges()

            success = True

        Catch ex As Exception
            MessageBox.Show("Error saving to Customer: " & vbCr & ex.ToString, "Save Data Error")
        End Try

    End Using

    Return success

End Function

Maintenant, remplacez ces deux lignes:

            If _accountTypeID = 0 Then db.AccountTypes.InsertOnSubmit(row)
            db.SubmitChanges()

avec quelque chose comme ça:

        Dim cmd As IDbCommand

        cmd = Me.Connection.CreateCommand()
        cmd.Transaction = Me.Transaction
        cmd.CommandText = query

        If myGuid.Trim.Length < 36 Then myGuid = UCase(System.Guid.NewGuid.ToString())
        cmd.Parameters.Add(New OleDbParameter("myGuid", row.myGuid))
        cmd.Parameters.Add(New OleDbParameter("AccountType", row.AccountType))
        cmd.Parameters.Add(New OleDbParameter("Description", row.Description))
        cmd.Parameters.Add(New OleDbParameter("AcHeadID", row.AcHeadID))
        cmd.Parameters.Add(New OleDbParameter("LastUpdated", Date.Now))
        If AccountTypeID > 0 Then cmd.Parameters.Add(New OleDbParameter("AccountTypeID", row.AccountTypeID))

        If Connection.State = ConnectionState.Closed Then Connection.Open()

        result = cmd.ExecuteNonQuery()

        cmd = Me.Connection.CreateCommand()
        cmd.Transaction = Me.Transaction
        cmd.CommandText = "SELECT @@IDENTITY"
        result = Convert.ToInt32(cmd.ExecuteScalar())

La dernière partie du code ci-dessus est l’objet de l’identification de l’enregistrement. Personnellement, j’en fais généralement une option, car dans la plupart des cas, je n’en ai pas besoin. Par conséquent, je n’ai pas besoin d’ajouter la surcharge de récupérer des données chaque fois qu’un enregistrement est sauvegardé, je suis heureux de connaître un l'enregistrement a été sauvegardé.

Il s’agit de la surcharge ajoutée à LinQ, ce qui entraîne l’insertion de Insert dans Fail. Est-ce vraiment nécessaire de l'avoir? Je ne pense pas.

Vous avez peut-être remarqué que je mettais normalement en place mes procédures de mise à jour et d'insertion, ce qui me permet de gagner du temps et de traiter les procédures d'insertion et de mise à jour en une fois.

Code de suppression:

Private Sub DelButton_Click(sender As Object, e As EventArgs) Handles DelButton.Click
    Using db As New AccessDataClassesDataContext(ACCCon)

        Dim AccountTypeID As Integer = Me.DataGridView1.CurrentRow.Cells(0).Value
        Dim row = From r In db.AccountTypes Where r.AccountTypeID = AccountTypeID

        For Each detail In row
            db.AccountTypes.DeleteOnSubmit(detail)
        Next

        Try
            db.SubmitChanges()
        Catch ex As Exception
            ' Provide for exceptions.
            MsgBox(ex)
        End Try

    End Using

End Sub

Vous pouvez maintenant profiter de LinQ to Access! Bonne codage :)

0
Hannington Mambo