使用LINQ和C#查询Microsoft Access MDB数据库

我有一个* .MDB数据库文件,我想知道是否有可能或推荐使用C#中的LINQ来处理它。 我也想知道一些简单的例子会是什么样子。

我对LINQ了解不多,但是我对这个任务的要求很简单(我相信)。 用户将传递给我的Microsoft Access MDB数据库的文件path,我想使用LINQ将行添加到数据库中的其中一个表。

你想要的是一个LINQ to ODBC提供者,或一个LINQ to JET / OLEDB提供者。

开箱即用,MS并没有做出一个。 有可能是第三方做的。

其实我最近(今天)发现你可以用LinqToSql访问Access数据库。 它必须是2002年或更新的格式,您将无法将表拖放到您的datacontext,所以无论是手动创build您的dbml中的对象,或者您可以使用SQL Server迁移到Access将其移动到SQL Server和然后拖放所有你想要的。 当你想要实际创build的上下文传递一个OleDbConnection。 在OleDbConnection上使用标准的Jet.OLEDB.4.0连接string,你就可以走了。 不确定这可能导致的限制。 我只是做了一个快速的样本,并没有问题OrderBy。

我写了一个小样本程序,用David的答案来testing。 您需要创build一个访问数据库并手动为Linq-to-SQL创buildDBML,因为您不能拖放它们。

插入失败,引用Missing semicolon (;) at end of SQL statement. 但查询似乎工作正常。

访问程序的数据库表

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

你可以使用一个DataSet。 有LINQ扩展,将允许您查询所有LINQ善良,我们已经用于:)的数据

 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只适用于SQL Server数据库。 你需要的是Microsoft Entity Framework。 这使得面向对象的访问你的MDB。 从这里你可以运行LINQ查询。

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

我在很多论坛上都看到过这个问题。 我已经做了一个尝试,对于那些一直在看的人来说,这是一个完整的答案。

LinQ不是为Access而开发的。 但是,许多查询将与Access一起使用,包括删除过程。 所以,据我所知,使用Access时只有两个重要的缺陷,它们是:

  1. 不能保存数据。
  2. 无法将对象拖放到dbml上

插入将失败,错误“缺less分号(;)”。 这是因为LinQ保存程序用于保存数据并检索一次保存的logging的主键ID。 我们知道你不能在Access中执行多个SQL语句,所以这就是失败的原因。

更新将失败,错误“logging未find”。 更新过程将导致查找要更新的logging,然后进行更新。 我不能说为什么它不会find它,当正常的LinQ查询find一个logging工作正常。

因为使用LinQ有很多好处,所以我想出了如何解决这个不足之处,同时在我的应用程序中享受其他好处。 这是如何(注:我的代码是在VB.net,但你可以转换,如果需要):

创buildLinQ to SQL(.dbml)类来pipe理您的LinQ对访问数据库,以及一种pipe理您的保存过程的方法。 下面是我创build的所有程序,我现在使用LinQ访问没有任何问题:

在表单上添加一个DataGridView 。 为添加,编辑和删除添加button

在这里输入图像描述

代码填写网格:

 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

在这里输入图像描述

代码来设置控制值

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

您将不得不手动添加数据对象到dbml,因为使用Access时不能拖放。 另外请注意,您将不得不在属性窗口中正确设置字段的所有属性。 添加字段时,不会设置几个属性。

在这里输入图像描述

代码保存

公共职能SaveAccountType(可选ByValtypes为string=“closures”)作为布尔值

  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 

现在replace这两行:

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

像这样的东西:

  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()) 

上面的代码的最后一部分是什么让你保存logging的ID。 就个人而言,我通常会做出这样的select,因为在大多数情况下我不需要它,所以我不需要增加每次保存logging时取回数据的开销,我很高兴知道logging被保存。

这是添加到LinQ的开销,这会导致插入与Access失败。 真的有必要吗? 我不这么认为。

您可能已经注意到我通常会将更新和插入过程放在一起,这样可以节省时间,同时解决插入和更新过程。

删除代码:

 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 

现在你可以享受LinQ访问! 快乐编码:)