24

So I'm trying to use Dapper.net and I'm liking it. What I'm not liking is when I try to batch-insert entities and I get the following error thrown:

Invalid type owner for DynamicMethod.

at System.Reflection.Emit.DynamicMethod.Init(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] signature, Type owner, Module m, Boolean skipVisibility, Boolean transparentMethod, StackCrawlMark& stackMark) at System.Reflection.Emit.DynamicMethod..ctor(String name, Type returnType, Type[] parameterTypes, Type owner, Boolean skipVisibility) at Dapper.SqlMapper.CreateParamInfoGenerator(Identity identity, Boolean checkForDuplicates, Boolean removeUnused, IList1 literals) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 3033 at Dapper.SqlMapper.GetCacheInfo(Identity identity, Object exampleParameters, Boolean addToCache) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 2138 at Dapper.SqlMapper.<QueryImpl>d__611.MoveNext() in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 1578 at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable1 commandTimeout, Nullable1 commandType) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 1479 at Dapper.SqlMapper.Query(IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable1 commandTimeout, Nullable1 commandType) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 1418 at NinjaEvaluation.Data.Database.DapperWrapper.<>c__DisplayClass41.b__3(SqlConnection sqlConnection, SqlTransaction transaction) in c:\Projects\InHouse\ninjaevaluation\NinjaEvaluation\NinjaEvaluation.Data\Database\DapperWrapper.cs:line 52 at NinjaEvaluation.Data.Database.DapperWrapper.Invoke(Action`2 action) in c:\Projects\InHouse\ninjaevaluation\NinjaEvaluation\NinjaEvaluation.Data\Database\DapperWrapper.cs:line 68

This happens in a completely normal situation when I run my query like this:

        string sql = @" INSERT INTO XXX
                        (XXXId, AnotherId, ThirdId, Value, Comment)
                        VALUES
                        (@XXXId, @AnotherId, @ThirdId, @Value, @Comment)";

        var parameters = command
            .MyModels
            .Select(model => new
            {
                XXXId= model.XXXId,
                AnotherId= model.AnotherId,
                ThirdId= model.ThirdId,
                Value = model.Value,
                Comment = model.Comment
            })
            .ToArray();

...

sqlConnection.Query(sql, parameters, commandType: commandType, transaction: transaction)

I found the following SO-thread started by someone having the same problem BUT the issue there seems to have been the .NET version (3.5) but I'm running .NET 4.5 and I can't figure out what the problem is.

Any suggestions?

Community
  • 1
  • 1
Maffelu
  • 2,018
  • 4
  • 24
  • 35

2 Answers2

41

I ran into this error when using an interface instead of the class:

Query<MyObject> worked, while Query<IMyObject> did not

Ryan
  • 3,127
  • 6
  • 32
  • 48
  • 6
    Same as me I ran into this error when using an interface instead of the class: Query worked, while Query did not. you should ensure that you use the class type on the return value for the executing command, not Interface. – Ammar Al-Habib Apr 01 '19 at 04:29
  • My issue was basically the same, but when trying to retrieve a list of objects, I mistakenly wrote `Query>` instead of `Query` – Lukas Oct 13 '22 at 14:39
17

It fails because this scenario using Query[<T>] isn't expecting an array / sequence of parameters. The Execute call-path does expect this, and unrolls the data automatically, executing the SQL once per item - but this isn't the case for Query[<T>], so it tries to create the dynamic method bound to the array (in your case), which isn't allowed. The code should probably detect this much earlier, and just say "nope, that isn't allowed".

You probably want to change your .ToArray() to .Single().

This will be clearer after the next build; the following passes:

    public void SO30435185_InvalidTypeOwner()
    {
        try {
            // not shown for brevity: something very similar to your code
            Assert.Fail();
        } catch(InvalidOperationException ex)
        {
            ex.Message.IsEqualTo("An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context");
        }
    }
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I should've used Execute :) – Maffelu May 26 '15 at 08:54
  • 1
    @Maffelu heh, or that ;p – Marc Gravell May 26 '15 at 11:09
  • Is this a shortcoming of Dapper, or does it logically not make sense to query with a sequence of parameters? [This question](http://stackoverflow.com/q/39884768) is interested in selecting the IDs of inserted rows, which is not possible in the current state of Dapper. [This question](http://stackoverflow.com/q/29615445) Implies it *used to* work - the first snippet - or is their error related to this? To me it would seem that if `Execute` can handle a sequence of parameters, `Query` should be able to as well. The best workaround I can come up with involves 4 separate queries, which isn't ideal – Rob Oct 05 '16 at 23:34
  • 1
    @Rob well, where the loop appears isn't going to make a huge difference to the actual operations; yes, we could make `Query` work with a sequence and essentially concatenate the results, but would it be any clearer? – Marc Gravell Oct 06 '16 at 11:52