【问题标题】:Query Microsoft Access MDB Database using LINQ and C#使用 LINQ 和 C# 查询 Microsoft Access MDB 数据库
【发布时间】:2010-09-22 16:27:25
【问题描述】:

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

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

【问题讨论】:

  • 您真的找到了与 ACCESS 合作的提供商吗?
  • 不,我只是决定在没有 LINQ to SQL 的情况下以无聊的方式来做。

标签: c# linq ms-access


【解决方案1】:

您需要的是 LINQ to ODBC 提供程序,或 LINQ to JET/OLEDB 提供程序。

MS 没有开箱即用。可能有第三者这样做。

【讨论】:

  • 我知道这是一个老话题,但几年前我创建了一个类似 EntityFramework 的库,可以让您使用 LINQ 直接访问 MDB。它的功能不完整,(更新实体还没有得到很好的支持)但是将表行作为对象检索可以正常工作accesstolinq.codeplex.com
【解决方案2】:

LINQ to SQL 仅适用于 SQL Server 数据库。您需要的是 Microsoft Entity Framework。这使得面向对象访问您的 mdb。从此您可以运行 LINQ 查询。

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

【讨论】:

  • 如何使用设计器对抗MDB?
  • 抱歉,但 EF 没有连接到 MDB
  • 当链接没有备份答案时,为什么会有投票?
  • 提供的答案链接不适用于所提出的问题。
  • 您只需要这样的提供者:jetentityframeworkprovider.codeplex.com
【解决方案3】:

其实我最近(今天)发现可以使用 LinqToSql 访问 Access 数据库。它必须是 2002 或更新的格式,您将无法将表拖放到数据上下文中,因此可以在 dbml 中手动创建对象,或者您可以使用 SQL Server Migration for Access 将其移动到 sql server 和然后拖放所有你想要的。当您想要实际创建上下文时,将其传递给 OleDbConnection。在 OleDbConnection 上使用标准 Jet.OLEDB.4.0 连接字符串,一切顺利。不确定这可能会产生的限制。我只是做了一个简单的示例,然后毫无问题地进行了 OrderBy。

【讨论】:

  • 并且不要忘记重命名designer.cs中的TableAttributes而不是所有者名称(例如:将dbo.Articles重命名为Articles)
  • 它充其量是残废 - 尝试运行插入给了我Missing semicolon (;) at end of SQL statement.
  • 是的,我做过一次,您可以使用开箱即用的 SELECT 查询,但几乎所有其他内容都必须在代码中定义为命令。工作量很大,但仍然比手工完成要好。
【解决方案4】:

您可以使用数据集。有一些 linq 扩展,可以让您使用我们已经习惯使用的所有 LINQ 优点查询数据:)

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

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

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

【讨论】:

    【解决方案5】:

    我编写了一个小示例程序来测试大卫的答案。您需要创建一个访问数据库并为 Linq-to-SQL 手动创建 DBML,因为您无法拖放它们。

    插入失败,引用 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();
                }
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

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

      LinQ 不适用于 Access。但是,许多查询都可以使用 Access,包括删除过程。因此,在我看来,使用 Access 时只有两个关键缺陷,它们是:

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

      插入将失败,并出现错误“缺少分号 (;)”。这是因为 LinQ 保存过程是为了保存数据并一次性检索保存的记录的主键 ID。我们知道您不能在 Access 中执行多个 SQL 语句,所以这就是失败的原因。

      更新将失败,并出现错误“找不到记录”。更新过程将寻找要更新的记录然后更新它。当正常的 LinQ 查询查找记录工作正常时,我不知道为什么它找不到它。

      因为使用 LinQ 有很多好处,我想出了如何解决这个缺陷,同时在整个应用程序中享受其他好处。就是这样(注意:我的代码在 VB.net 中,但您可以根据需要进行转换):

      创建 LinQ to SQL (.dbml) 类来根据访问数据库管理 LinQ,以及管理保存过程的方法。以下是我创建的完整过程,我现在使用 LinQ 访问没有任何问题:

      在表单上添加DataGridView。添加用于添加、编辑和删除的按钮

      填充网格的代码:

      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
      

      详细表格

      设置控制值的代码

      私有子 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 时无法拖放。另请注意,您必须在属性窗口中正确设置字段的所有属性。添加字段时没有设置几个属性。

      要保存的代码

      Public Function SaveAccountType(Optional ByVal type As String = "关闭") 作为布尔值

          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
      

      现在替换这两行:

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

      上面代码的最后一部分是获取保存记录的 ID。就个人而言,我通常会选择这个选项,因为在大多数情况下我不需要它,所以我不需要在每次保存记录时添加取回数据的开销,我很高兴知道一个记录已保存。

      这是添加到 LinQ 的开销,这会导致 Insert 使用 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 访问了!快乐编码:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-09
        • 2011-03-15
        • 2011-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-12
        相关资源
        最近更新 更多