Consultar la base de datos MDB de Microsoft Access utilizando LINQ y C #

Tengo un archivo de base de datos * .MDB, y me pregunto si es posible o recomendable trabajar en su contra con LINQ en C #. También me pregunto cómo serían algunos ejemplos simples.

No sé mucho sobre LINQ, pero mis requisitos para esta tarea son bastante simples (creo). El usuario me pasará una ruta de archivo a la base de datos Microsoft Access MDB y me gustaría usar LINQ para agregar filas a una de las tablas dentro de la base de datos.

Lo que desea es un proveedor de LINQ a ODBC o un proveedor de LINQ a JET / OLEDB.

Fuera de la caja, MS no hace uno. Puede haber un tercero que sí.

De hecho, recientemente (hoy) descubrí que puede acceder a una base de datos de Access con LinqToSql. Debe estar en el formato 2002 o posterior, no podrá arrastrar y soltar las tablas a su contexto de datos para crear manualmente los objetos en su dbml o puede usar la migración de SQL Server para Access para moverlo a un servidor SQL y luego arrastra y suelta todo lo que quieras. Cuando desee crear el contexto, páselo OleDbConnection. Use su cadena de conexión estándar Jet.OLEDB.4.0 en OleDbConnection y estará listo. Sin embargo, no estoy seguro de la limitación en la que puede incurrir. Acabo de hacer una muestra rápida e hice un OrderBy sin problema.

Escribí un pequeño progtwig de muestra para probar esto con la respuesta de David. Tendrá que crear una base de datos de acceso y crear manualmente el DBML para Linq-to-SQL, ya que no puede arrastrar y soltarlos.

Las inserciones fallan, citando el Missing semicolon (;) at end of SQL statement. pero las consultas parecen funcionar bien.

Acceda a tablas de bases de datos para el

 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 projects = new List(); 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(); } } } } 

Puedes usar un DataSet. Hay extensiones de linq que te permitirán consultar los datos con todo el bien de LINQ que hemos usado 🙂

 eICATDataSet.ICSWSbuDataTable tbl = new eICATDataSet.ICSWSbuDataTable(); ICSWSbuTableAdapter ta = new ICSWSbuTableAdapter(); ta.Fill(tbl); var res = tbl.Select(x => x.ProcedureDate.Year == 2010); 

LINQ to SQL solo funciona para las bases de datos de SQL Server. Lo que necesita es Microsoft Entity Framework. Esto hace que el acceso orientado a objetos a su mdb. A partir de esto, puede ejecutar consultas LINQ.

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

He visto mucho esta pregunta y en varios foros. Intenté y aquí hay una respuesta completa para aquellos que lo han estado buscando.

LinQ no fue hecho para Access. Sin embargo, muchas de las consultas funcionarán con Access, incluido el procedimiento de eliminación. Entonces, según mi opinión, solo hay 2 deficiencias cruciales cuando se trabaja con Access, que son:

  1. no ser capaz de guardar datos.
  2. no ser capaz de arrastrar y soltar objetos en el dbml

La inserción fallará con el error “falta punto y coma (;)”. Esto se debe a que el procedimiento de guardado de LinQ se realizó para guardar datos y recuperar el ID de clave principal del registro guardado de una vez. Sabemos que no puede ejecutar múltiples sentencias de SQL en Access, por lo que esa es la razón de esa falla.

La actualización fallará con el error “registro no encontrado”. Un procedimiento de actualización hará que el registro se actualice y luego se actualice. No puedo decir por qué no lo encontraría, cuando la consulta normal de LinQ para encontrar un registro funciona bien.

Debido a que hay tanto beneficio en usar LinQ, descubrí cómo evitar la deficiencia, mientras disfruto de los otros beneficios a lo largo de mi aplicación. Así es como (NB: Mis códigos están en VB.net, pero puede convertirlos si es necesario):

Cree la clase LinQ a SQL (.dbml) para administrar su LinQ contra la base de datos de acceso y una forma de administrar su procedimiento de guardado. Debajo están los procedimientos completos de lo que creé y ahora trabajo con LinQ para acceder sin ningún problema:

Agregue un DataGridView en un formulario. Agregar botones para Agregar, Editar y Eliminar

enter image description here

Código para llenar la grilla:

 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

Código para establecer valores de control

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

Deberá agregar los objetos de datos al dbml manualmente ya que no puede arrastrar y soltar cuando usa Access. También tenga en cuenta que deberá establecer correctamente todas las propiedades de los campos en la ventana de propiedades. Varias propiedades no se establecen cuando agrega los campos.

enter image description here

Código para guardar

Función pública SaveAccountType (opcional ByVal tipo As String = “Cerrar”) Como booleano

  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 

Ahora reemplace estas dos líneas:

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

con algo como esto:

  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 última parte del código anterior es lo que le permite obtener la ID del registro guardado. Personalmente, generalmente lo hago una opción, porque no lo necesito en la mayoría de los casos, así que no necesito agregar esa sobrecarga de recuperar datos cada vez que se guarda un registro, estoy feliz de saber que registro fue guardado

Esa es la sobrecarga agregada a LinQ, que hace que el complemento falle con Access. ¿Es realmente necesario tenerlo? No lo creo.

Puede haber notado que normalmente pongo mis procedimientos de Actualización e Insertar juntos, de modo que eso me ahorra tiempo y ha abordado los procedimientos de Insertar y Actualizar de una vez.

Código para Eliminar:

 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 

¡Ahora puedes disfrutar de LinQ para acceder! Feliz encoding 🙂