Dapper Extension Ms Access System.Data.OleDb.OleDbException
Asked Answered
M

1

2

I just started to use Dapper. Dapper works fine. As a next step when I tried to integrate with Dapper Extension. It generates an exception called System.Data.OleDb.OleDbException "Additional information: Characters found after end of SQL statement." Why is that? Dapper Extension doesn't support Ms Access (because of the end character) or problem with my code or I am missing something. My code is below

using (var conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=myAccessFile.accdb;"))
{
    conn.Open();
    conn.Insert<Person>(new Person { Name = "John Stan", Age = 20 });
}
Melleta answered 29/8, 2016 at 5:43 Comment(3)
Try to find a way to determine the SQL that Dapper Extensions is generating. Perhaps it is combining the INSERT and the SELECT @@IDENTITY statements in a single call. If so, then the Access Database Engine will throw the error you cited.Kashakashden
You may be able to write your own SqlDialect class for MSAccess that implements DapperExtensions SqlDialectBase. You could then implement the GetIdentitySql method with an Access specific SQL statement to get the ID of your latest record. you could then stick with Dapper Extensions. There are 5 implementations for the main SQL engines in the GitHub repo @ github.com/tmsmith/Dapper-Extensions/tree/master/…Trellis
@G Davison github.com/tmsmith/Dapper-Extensions/issues/79Melleta
T
8

According to an MSDN article,

Some database engines, such as the Microsoft Access Jet database engine, do not support output parameters and cannot process multiple statements in a single batch.

So the problem is that the Insert method is generating a statement such as

INSERT INTO [Person] ([Person].[PersonName]) VALUES (@PersonName);
SELECT CAST(SCOPE_IDENTITY()  AS BIGINT) AS [Id]

and Access can't deal with it.

Reading around, it seems like that are various suggestions as to how to do insert-and-get-new-record-key when dealing with Access (that MSDN article suggests a second SELECT statement) but that doesn't help if you're using the DapperExtensions library, since that is what generates the query for you.

So, basically, I think that you are correct in thinking that DapperExtensions won't work with Access.


On a side note, I had a nightmare trying to find out what queries were being generated. There are various articles that talk about a registry hack to set a "JETSHOWPLAN" value to "ON" but I couldn't make any of them work. In the end, I created wrapped database connection and command classes so that the queries could be captured on the way out. In case this is of any use to anyone in the future, I'm including it below..

The database connection initialisation code needs to change slightly - eg.

var connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Database2.mdb;";
using (var conn = new WrappedDbConnection(new OleDbConnection(connectionString)))
{
    conn.Insert<Person>(new Person { PersonName = "Dan" });
}

and the following two classes need to be defined -

public class WrappedDbConnection : IDbConnection
{
    private readonly IDbConnection _conn;
    public WrappedDbConnection(IDbConnection connection)
    {
        if (connection == null)
            throw new ArgumentNullException(nameof(connection));

        _conn = connection;
    }

    public string ConnectionString
    {
        get { return _conn.ConnectionString; }
        set { _conn.ConnectionString = value; }
    }

    public int ConnectionTimeout
    {
        get { return _conn.ConnectionTimeout; }
    }

    public string Database
    {
        get { return _conn.Database; }
    }

    public ConnectionState State
    {
        get { return _conn.State; }
    }

    public IDbTransaction BeginTransaction()
    {
        return _conn.BeginTransaction();
    }

    public IDbTransaction BeginTransaction(IsolationLevel il)
    {
        return _conn.BeginTransaction(il);
    }

    public void ChangeDatabase(string databaseName)
    {
        _conn.ChangeDatabase(databaseName);
    }

    public void Close()
    {
        _conn.Close();
    }

    public IDbCommand CreateCommand()
    {
        return new WrappedDbCommand(_conn.CreateCommand());
    }

    public void Dispose()
    {
        _conn.Dispose();
    }

    public void Open()
    {
        _conn.Open();
    }
}

public class WrappedDbCommand : IDbCommand
{
    private readonly IDbCommand _cmd;
    public WrappedDbCommand(IDbCommand command)
    {
        if (command == null)
            throw new ArgumentNullException(nameof(command));

        _cmd = command;
    }

    public string CommandText
    {
        get { return _cmd.CommandText; }
        set { _cmd.CommandText = value; }
    }

    public int CommandTimeout
    {
        get { return _cmd.CommandTimeout; }
        set { _cmd.CommandTimeout = value; }
    }

    public CommandType CommandType
    {
        get { return _cmd.CommandType; }
        set { _cmd.CommandType = value; }
    }

    public IDbConnection Connection
    {
        get { return _cmd.Connection; }
        set { _cmd.Connection = value; }
    }

    public IDataParameterCollection Parameters
    {
        get { return _cmd.Parameters; }
    }

    public IDbTransaction Transaction
    {
        get { return _cmd.Transaction; }
        set { _cmd.Transaction = value; }
    }

    public UpdateRowSource UpdatedRowSource
    {
        get { return _cmd.UpdatedRowSource; }
        set { _cmd.UpdatedRowSource = value; }
    }

    public void Cancel()
    {
        _cmd.Cancel();
    }

    public IDbDataParameter CreateParameter()
    {
        return _cmd.CreateParameter();
    }

    public void Dispose()
    {
        _cmd.Dispose();
    }

    public int ExecuteNonQuery()
    {
        Console.WriteLine($"[ExecuteNonQuery] {_cmd.CommandText}");
        return _cmd.ExecuteNonQuery();
    }

    public IDataReader ExecuteReader()
    {
        Console.WriteLine($"[ExecuteReader] {_cmd.CommandText}");
        return _cmd.ExecuteReader();
    }

    public IDataReader ExecuteReader(CommandBehavior behavior)
    {
        Console.WriteLine($"[ExecuteReader({behavior})] {_cmd.CommandText}");
        return _cmd.ExecuteReader();
    }

    public object ExecuteScalar()
    {
        Console.WriteLine($"[ExecuteScalar] {_cmd.CommandText}");
        return _cmd.ExecuteScalar();
    }

    public void Prepare()
    {
        _cmd.Prepare();
    }
}

Now, the queries are written to the console before being sent to the database.

Topfull answered 2/9, 2016 at 12:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.