2008-11-17 30 views
36

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.Consultar base de datos MDB de Microsoft Access utilizando LINQ y C#

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.

+0

¿Encontraste realmente un proveedor para trabajar con ACCESS? –

+1

No, simplemente decidí hacerlo de forma aburrida sin LINQ to SQL. –

Respuesta

14

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

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

+0

Entiendo que este es un tema antiguo, pero creé una biblioteca tipo EntityFramework hace unos años que puede permitirle usar LINQ para acceder a MDB directamente. No es una característica completa, (las entidades de actualización aún no están bien respaldadas), pero recuperar filas de la tabla como objetos funciona bien https://accesstolinq.codeplex.com/ – EnderWiggin

0

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

+5

¿Cómo usar el diseñador contra el MDB? –

+4

sory pero EF no se conecta a MDB –

+11

¿Por qué esto tiene votos ascendentes cuando el enlace no respalda la respuesta? – fuzz

13

En realidad, yo recientemente (hoy) descubrieron que se puede acceder a una base de datos 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.

+1

y no olvide cambiar el nombre de TableAttributes en el designer.cs sin el nombre del propietario (p. ej., cambiar el nombre de dbo.Articles a Articles) – Gregoire

+0

Está lisiado en el mejor de los casos: el intento de ejecutar una inserción me ha dado 'Falta el punto y coma (;) al final de la declaración de SQL' – jocull

+0

Sí, lo he hecho una vez, puede usar las consultas SELECT de fábrica, pero prácticamente todo lo demás tendrá que definirse en el código como un comando. Es mucho trabajo, pero aún mejor que hacerlo todo a mano. –

1

Puede usar un DataSet. Hay extensiones de LINQ que le permitirá consultar los datos con toda la bondad que nos hemos convertido en LINQ uso a :)

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

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

var res = tbl.Select(x => x.ProcedureDate.Year == 2010); 
7

me escribió un pequeño programa de ejemplo 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 Missing semicolon (;) at end of SQL statement. pero las consultas parecen funcionar bien.

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(); 
      } 
     } 
    } 
} 
0

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

LinQ no está hecho para el acceso. Sin embargo, muchas de las consultas funcionarán con Access, incluido el procedimiento de eliminación. Por lo tanto, en mi opinión, sólo hay 2 deficiencias cruciales cuando se trabaja con Access, que son:

  1. no ser capaz de guardar los datos.
  2. no ser capaz de arrastrar y soltar objetos sobre la dbml

Insertar 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.

Porque 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. Esta es la forma (NB: Mis códigos están en VB.net, pero se puede convertir, si es necesario):

Crear el LINQ to SQL (.dbml) de clase para manejar su LinQ contra la base de datos de acceso, y una manera de gerente 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 DataGridView en un formulario. Agrega botones para Añadir, Editar Borrar &

enter image description here

código para rellenar la cuadrícula:

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 contro l, los valores

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

Usted tendrá que añadir los objetos de datos a la dbml manualmente ya que no puede arrastrar y soltar cuando se utiliza acceso. 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 SaveAccountType Pública (tipo opcional ByVal como secuencia = "Cerrar") 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 

Ahora reemplazar 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 se obtiene el 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 la inserción falle con acceso. ¿Es realmente necesario tenerlo? No lo creo.

Es posible que haya notado que normalmente pongo mis procedimientos de actualización e inserción juntos, de modo que eso me ahorra tiempo y ha solucionado los procedimientos de actualización Insert & 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 puede disfrutar de LINQ to Accede! Happy coding :)

Cuestiones relacionadas