Ответ 1
Для вас нужен поставщик LINQ to ODBC или поставщик LINQ to JET/OLEDB.
Из коробки MS не делает этого. Это может быть и третья сторона.
У меня есть файл базы данных *.MDB, и мне интересно, возможно ли это или рекомендуется работать против него, используя LINQ в С#. Мне также интересно, как выглядят простые примеры.
Я не знаю много о LINQ, но мои требования к этой задаче довольно просты (я считаю). Пользователь передаст мне путь к базе данных Microsoft Access MDB, и я хотел бы использовать LINQ для добавления строк в одну из таблиц в базе данных.
Для вас нужен поставщик LINQ to ODBC или поставщик LINQ to JET/OLEDB.
Из коробки MS не делает этого. Это может быть и третья сторона.
На самом деле я недавно (сегодня) обнаружил, что вы можете получить доступ к базе данных Access с помощью LinqToSql. Он должен быть в формате 2002 или более поздний, вы не сможете перетаскивать таблицы в свой файл datacontext, чтобы вручную создавать объекты в своем dbml, или вы можете использовать миграцию SQL Server для доступа, чтобы переместить его на сервер sql и затем перетащите все, что хотите. Если вы хотите создать контекст, передайте его OleDbConnection. Используйте стандартную строку соединения Jet.OLEDB.4.0 на OleDbConnection, и вам хорошо идти. Не уверен в ограничениях, которые могут возникнуть. Я просто сделал быстрый образец и сделал OrderBy без проблем.
Я написал небольшую пробную программу, чтобы проверить это с ответом Дэвида. Вам нужно создать базу данных доступа и вручную создать DBML для Linq-to-SQL, так как вы не можете перетащить их.
Вставки завершаются неудачно, ссылаясь на 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 есть только 2 основных недостатка:
Вставка завершится с ошибкой "Отсутствие точки с запятой (;)". Это связано с тем, что была выполнена процедура сохранения LinQ для сохранения данных и получения идентификатора первичного ключа записи, сохраненной за один раз. Мы знаем, что вы не можете выполнять несколько операторов SQL в Access, поэтому причина этого отказа.
Обновление завершится с ошибкой "запись не найдена". Процедура обновления вызовет поиск обновляемой записи, а затем обновит ее. Я не могу сказать, почему он не найдет его, когда нормальный запрос LinQ для поиска записи отлично работает.
Поскольку в LinQ так много преимуществ, я выяснил, как обойти недостаток, а также пользоваться другими преимуществами во всем приложении. Вот как (NB: Мои коды находятся на 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
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. Также обратите внимание, что вам нужно будет установить все свойства полей правильно в окнах свойств. При добавлении полей некоторые свойства не задаются.
Код для сохранения
Public Function SaveAccountType (необязательный тип ByVal 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())
Последняя часть приведенного выше кода - это то, что дает вам идентификатор сохраненной записи. Лично я обычно делаю этот вариант, потому что в большинстве случаев он не нужен, поэтому мне не нужно добавлять накладные расходы на сбор данных за каждый раз, когда сохраняется запись, я рад, что знаю запись была сохранена.
Это накладные расходы, добавленные в LinQ, что приводит к сбою вставки с доступом. Нужно ли это иметь? Я так не думаю.
Возможно, вы заметили, что я обычно добавляю свои процедуры обновления и вставки вместе, так что это экономит время и имеет адрес как вставки, так и обновление за один раз.
Код для удаления:
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 для доступа! Счастливое кодирование:)