{"record":{"id":"ee715bb8c5d5a10a","repo":"DapperLib/Dapper","slug":"this-operation-requires-an-identity-or-a-connected","errorCode":null,"errorMessage":"This operation requires an identity or a connected command","messagePattern":"This operation requires an identity or a connected command","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Dapper/SqlMapper.GridReader.cs","lineNumber":57,"sourceCode":"                cancel = cancellationToken;\r\n            }\r\n\r\n            internal GridReader(IDbCommand command, DbDataReader reader, Identity identity, IParameterCallbacks? callbacks, bool addToCache,\r\n                CancellationToken cancellationToken = default)\r\n                : this(command, reader, identity, callbacks is null ? null : static state => ((IParameterCallbacks)state!).OnCompleted(),\r\n                      callbacks, addToCache, cancellationToken)\r\n            { }\r\n\r\n            private Identity Identity => _identity ??= CreateIdentity();\r\n\r\n            private Identity CreateIdentity()\r\n            {\r\n                var cmd = Command;\r\n                if (cmd is not null && cmd.Connection is not null)\r\n                {\r\n                    return new Identity(cmd.CommandText, cmd.CommandType, cmd.Connection, null, null);\r\n                }\r\n                throw new InvalidOperationException(\"This operation requires an identity or a connected command\");\r\n            }\r\n\r\n            /// <summary>\r\n            /// Read the next grid of results, returned as a dynamic object.\r\n            /// </summary>\r\n            /// <param name=\"buffered\">Whether the results should be buffered in memory.</param>\r\n            /// <remarks>Note: each row can be accessed via \"dynamic\", or by casting to an IDictionary&lt;string,object&gt;</remarks>\r\n            public IEnumerable<dynamic> Read(bool buffered = true) => ReadImpl<dynamic>(typeof(DapperRow), buffered);\r\n\r\n            /// <summary>\r\n            /// Read an individual row of the next grid of results, returned as a dynamic object.\r\n            /// </summary>\r\n            /// <remarks>Note: the row can be accessed via \"dynamic\", or by casting to an IDictionary&lt;string,object&gt;</remarks>\r\n            public dynamic ReadFirst() => ReadRow<dynamic>(typeof(DapperRow), Row.First);\r\n\r\n            /// <summary>\r\n            /// Read an individual row of the next grid of results, returned as a dynamic object.\r\n            /// </summary>\r","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/DapperLib/Dapper/blob/72a54c475f75e18cb93cba0809d00a5e6e49efd9/Dapper/SqlMapper.GridReader.cs#L39-L75","documentation":"GridReader builds an Identity lazily (SqlMapper.GridReader.cs:48) to key the per-shape deserializer cache. CreateIdentity() (line 50) can only build one from a non-null Command whose Connection is non-null; otherwise it throws InvalidOperationException. This fires when no Identity was supplied at construction and the command is detached/disposed before any Read.","triggerScenarios":"Construct a GridReader via the protected/internal constructor with identity=null and either Command=null or Command.Connection=null, then call Read/ReadFirst/ReadAsync which dereferences Identity. Custom subclasses or test doubles that bypass the normal QueryMultiple path hit this.","commonSituations":"Subclassing GridReader or writing a fake/wrapper without supplying an Identity; disposing/closing the connection before consuming the grids; capturing the command, detaching it, then reading.","solutions":["Keep the IDbConnection open and the command alive until every grid is consumed; wrap usage in using var grid = cnn.QueryMultiple(...).","When constructing a GridReader manually, pass a valid Identity built from the command text, type, and connection.","Prefer the provided QueryMultiple/QueryMultipleAsync factory over manual construction so Identity is always supplied."],"exampleFix":"// before\nvar grid = new MyGridReader(cmd, reader, identity: null); // cmd.Connection later set null\nvar data = grid.Read<Foo>(); // throws\n\n// after\nvar identity = new Identity(cmd.CommandText, cmd.CommandType, cmd.Connection, null, null);\nvar grid = new MyGridReader(cmd, reader, identity);\nvar data = grid.Read<Foo>();","handlingStrategy":"validation","validationCode":"// Before reading, ensure identity can be built\nvar cmd = grid.Command;\nif (cmd is null || cmd.Connection is null) throw new InvalidOperationException(\"GridReader needs a connected command before reading\");","typeGuard":null,"tryCatchPattern":"try { var data = grid.Read<Foo>(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"identity or a connected command\"))\n{ /* re-open connection / re-run QueryMultiple */ }","preventionTips":["Use QueryMultiple/QueryMultipleAsync factories so Identity is always supplied.","Keep the connection open inside a using until all grids are read.","If subclassing GridReader, always pass a valid Identity to the constructor."],"tags":["dapper","gridreader","identity","invalidoperationexception","connection-lifecycle"],"backgroundTag":null,"analyzedSha":"72a54c475f75e18cb93cba0809d00a5e6e49efd9","analyzedAt":"2026-08-13T14:18:18.115Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}