DapperLib / DapperLib/Dapper.Contrib

Dapper Contrib Insert method return a long as inserted Id but internally is using an int

Open
#42 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
293
Forks
109
PR merge metrics
No merged PRs in 30d

Description

This is the current Insert method:

public static long Insert<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
    {
        var isList = false;

        var type = typeof(T);

        if (type.IsArray)
        {
            isList = true;
            type = type.GetElementType();
        }
        else if (type.IsGenericType)
        {
            var typeInfo = type.GetTypeInfo();
            bool implementsGenericIEnumerableOrIsGenericIEnumerable =
                typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||
                typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);

            if (implementsGenericIEnumerableOrIsGenericIEnumerable)
            {
                isList = true;
                type = type.GetGenericArguments()[0];
            }
        }

        var name = GetTableName(type);
        var sbColumnList = new StringBuilder(null);
        var allProperties = TypePropertiesCache(type);
        var keyProperties = KeyPropertiesCache(type);
        var computedProperties = ComputedPropertiesCache(type);
        var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList();

        var adapter = GetFormatter(connection);

        for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
        {
            var property = allPropertiesExceptKeyAndComputed[i];
            adapter.AppendColumnName(sbColumnList, property.Name);  //fix for issue DapperLib/Dapper#336
            if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                sbColumnList.Append(", ");
        }

        var sbParameterList = new StringBuilder(null);
        for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++)
        {
            var property = allPropertiesExceptKeyAndComputed[i];
            sbParameterList.AppendFormat("@{0}", property.Name);
            if (i < allPropertiesExceptKeyAndComputed.Count - 1)
                sbParameterList.Append(", ");
        }

        int returnVal;
        var wasClosed = connection.State == ConnectionState.Closed;
        if (wasClosed) connection.Open();

        if (!isList)    //single entity
        {
            returnVal = adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(),
                sbParameterList.ToString(), keyProperties, entityToInsert);
        }
        else
        {
            //insert list of entities
            var cmd = $"insert into {name} ({sbColumnList}) values ({sbParameterList})";
            returnVal = connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
        }
        if (wasClosed) connection.Close();
        return returnVal;
    }

And the internal Insert (calling in the line returnVal = adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString()) is the follow:

public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, string tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
{
    var cmd = $"insert into {tableName} ({columnList}) values ({parameterList});select SCOPE_IDENTITY() id";
    var multi = connection.QueryMultiple(cmd, entityToInsert, transaction, commandTimeout);

    var first = multi.Read().FirstOrDefault();
    if (first == null || first.id == null) return 0;

    var id = (int)first.id;
    var propertyInfos = keyProperties as PropertyInfo[] ?? keyProperties.ToArray();
    if (propertyInfos.Length == 0) return id;

    var idProperty = propertyInfos[0];
    idProperty.SetValue(entityToInsert, Convert.ChangeType(id, idProperty.PropertyType), null);

    return id;
}

As you can see, that returns an int. But, the public static long Insert returns a long (this method is calling to the previews). I think that this is a bug. I defined all my Ids as long and in the database as a bigint. What about if I insert a row that the Id will be greater than int.MaxValue? A System.OverflowException is triggered (Value too large or too small for Int32). This is the System.OverflowException´s StackTrace:

en System.Decimal.ToInt32(Decimal d)
en System.Decimal.op_Explicit(Decimal value)
en System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, 
T0 arg0)
en SqlServerAdapter.Insert(IDbConnection connection, IDbTransaction 
transaction, Nullable`1 commandTimeout, String tableName, String columnList, 
String parameterList, IEnumerable`1 keyProperties, Object entityToInsert)
en Dapper.Contrib.Extensions.SqlMapperExtensions.Insert[T](IDbConnection 
connection, T entityToInsert, IDbTransaction transaction, Nullable`1 
commandTimeout)

Thanks,

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the public generic Insert and the adapter Insert method shown in the issue, then trace the SQL Server adapter's identity result handling. Add regression coverage for inserting an entity with a bigint identity above Int32.MaxValue, and verify that the returned value and entity key are preserved without overflow.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
databases
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.