{"record":{"id":"d5ecf85f7d1846fb","repo":"DapperLib/Dapper","slug":"multiexec-is-not-supported-by-executereader","errorCode":null,"errorMessage":"MultiExec is not supported by ExecuteReader","messagePattern":"MultiExec is not supported by ExecuteReader","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"Dapper/SqlMapper.cs","lineNumber":3081,"sourceCode":"            finally\r\n            {\r\n                if (wasClosed) cnn.Close();\r\n                if (cmd is not null && disposeCommand)\r\n                {\r\n                    cmd.Parameters.Clear();\r\n                    cmd.Dispose();\r\n                }\r\n            }\r\n        }\r\n\r\n        private static Action<IDbCommand, object?>? GetParameterReader(IDbConnection cnn, ref CommandDefinition command)\r\n        {\r\n            object? param = command.Parameters;\r\n            IEnumerable? multiExec = GetMultiExec(param);\r\n            CacheInfo? info = null;\r\n            if (multiExec is not null)\r\n            {\r\n                throw new NotSupportedException(\"MultiExec is not supported by ExecuteReader\");\r\n            }\r\n\r\n            // nice and simple\r\n            if (param is not null)\r\n            {\r\n                var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param.GetType());\r\n                info = GetCacheInfo(identity, param, command.AddToCache);\r\n            }\r\n            var paramReader = info?.ParamReader;\r\n            return paramReader;\r\n        }\r\n\r\n        private static Func<DbDataReader, object> GetSimpleValueDeserializer(Type type, Type effectiveType, int index, bool useGetFieldValue)\r\n        {\r\n            // no point using special per-type handling here; it boils down to the same, plus not all are supported anyway (see: SqlDataReader.GetChar - not supported!)\r\n#pragma warning disable 618\r\n            if (type == typeof(char))\r\n            { // this *does* need special handling, though\r","sourceCodeStart":3063,"sourceCodeEnd":3099,"githubUrl":"https://github.com/DapperLib/Dapper/blob/72a54c475f75e18cb93cba0809d00a5e6e49efd9/Dapper/SqlMapper.cs#L3063-L3099","documentation":"Thrown by GetParameterReader (the ExecuteReader code path) when the `param` object is a multi-exec enumerable. Only `Execute`/`ExecuteAsync` are designed to loop over a sequence and run the command once per item; the reader paths (ExecuteReader/ExecuteReaderAsync, and the Query methods built on them) take a single command, so passing a list is rejected with NotSupportedException.","triggerScenarios":"Calling `cnn.ExecuteReader(sql, new[] { new {Id=1}, new {Id=2} })` or `ExecuteReaderAsync` with an array/list as `param`. GetMultiExec (SqlMapper.cs:623) classifies it as a multi-exec sequence, and GetParameterReader (SqlMapper.cs:3081) refuses it.","commonSituations":"Trying to batch-insert via ExecuteReader instead of Execute; passing a collection of DTOs expecting per-row reader execution; misunderstanding which Dapper methods support multi-exec.","solutions":["Use `Execute`/`ExecuteAsync` for batch (multi-exec) operations — that is the only method that iterates a sequence.","If you need a reader, call ExecuteReader once per item in your own loop, or unwrap the sequence into a single parameter object.","For bulk inserts, use `Execute` with the list, or a bulk-insert facility of your provider.","Pass an anonymous object (`new { ... }`) rather than a list if you want a single reader call."],"exampleFix":"// before\nusing var rd = cnn.ExecuteReader(\"insert into t(id) values(@Id)\", listOfDtos);\n// after\ncnn.Execute(\"insert into t(id) values(@Id)\", listOfDtos);","handlingStrategy":"validation","validationCode":"// Use Execute for batch/multi-exec; unwrap lists before ExecuteReader.\nif (param is IEnumerable and not string and not IDynamicParameters) cnn.Execute(sql, param);\nelse { using var rd = cnn.ExecuteReader(sql, param); }","typeGuard":"static bool IsMultiExec(object? p) => p is IEnumerable and not string and not IDynamicParameters and not IEnumerable<KeyValuePair<string,object>>;","tryCatchPattern":null,"preventionTips":["Use Execute/ExecuteAsync for per-item batch operations.","Pass a single parameter object (not a list) to ExecuteReader.","Document which Dapper methods support multi-exec."],"tags":["dapper","executereader","multiexec","parameters"],"backgroundTag":null,"analyzedSha":"72a54c475f75e18cb93cba0809d00a5e6e49efd9","analyzedAt":"2026-08-13T14:18:18.115Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}