Dapper + MSAccess: How to get identifier of insert

2019-02-25 11:50发布

I am using Dapper with C# and back end is MS Access. My DAL method inserts record in database. I want to return unique identifier (or updated POCO with unique identifier) of the inserted row. I am expecting my function something like follows (I know this does not work; just to explain what I want): -

public MyPoco Insert(MyPoco myPoco)
{
    sql = @"INSERT INTO MyTable (Field1, Field2) VALUES (@Field1, @Field2)";
    var param = GetMappedParams(myPoco);//ID property here is null.
    var result = _connection.Query<MyPoco>(sql, param, null, false, null, CommandType.Text);.Single();
    return result;//This result now contains ID that is created by database.
}

I am from NHibernate world and POCO updates automatically with NH. If not; we can call Refresh method and it updates the ID. I am not aware how to achieve this with Dapper.

I read this question on SO which is not relevant as it talks about SQL Server.

Another this question does not have accepted answer.

I read this question where accepted answer explains pit-falls of using @@Identity.

2条回答
放我归山
2楼-- · 2019-02-25 12:05

This is what works for me:

static MyPoco Insert(MyPoco myPoco)
{
    string sql = "INSERT INTO MyTable (Field1, Field2) VALUES (@Field1, @Field2)";
    _connection.Execute(sql, new {myPoco.Field1, myPoco.Field2});
    myPoco.ID = _connection.Query<int>("SELECT @@IDENTITY").Single();
    return myPoco;  // This result now contains ID that is created by database.
}

Note that this will work with an OleDbConnection to the Access database, but it will not work with an OdbcConnection.

Edit re: comment

To ensure that the Connection remains open between the INSERT and the SELECT calls, we could do this:

static void Insert(MyPoco myPoco)
{
    string sql = "INSERT INTO MyTable (Field1, Field2) VALUES (@Field1, @Field2)";
    bool connAlreadyOpen = (_connection.State == System.Data.ConnectionState.Open);
    if (!connAlreadyOpen)
    {
        _connection.Open();
    }
    _connection.Execute(sql, new {myPoco.Field1, myPoco.Field2});
    myPoco.ID = _connection.Query<int>("SELECT @@IDENTITY").Single();
    if (!connAlreadyOpen)
    {
        _connection.Close();
    }
    return;  // (myPoco now contains ID that is created by database.)
}
查看更多
劫难
3楼-- · 2019-02-25 12:09

Just a couple of extra thoughts: If the @@Identity pitfalls are an issue then another option would be to create a new GUID ahead of time in code and then insert that GUID with the rest of the data, rather than letting Access create the identity value when it creates the new record.

I appreciate that will only work if your particular situation allows for a GUID primary key for the table, but it does guarantee you that you know the true value of the key for the record you just inserted.

Alternatively, if you don't want a GUID key you could create a table with a single row that holds the current seed value for any manually managed keys in your application. You can then manually increment the particular seed's value each time you want to insert a new record. As with the GUID approach, you'd then manually insert the ID with the record, this time the ID would be the newly incremented seed you just retrieved.

Again, that should guarantee you a unique key for each insert, although now you are doing a read and two writes for each insert.

查看更多
登录 后发表回答