{"record":{"id":"7671715481e7fcab","repo":"DapperLib/Dapper","slug":"attempting-to-cast-a-dbnull-to-a-non-nullable-type","errorCode":null,"errorMessage":"Attempting to cast a DBNull to a non nullable type! Note that out/return parameters will not have updated values until the data stream completes (after the 'foreach' for Query(..., buffered: false), or after the GridReader has been disposed for QueryMultiple)","messagePattern":"Attempting to cast a DBNull to a non nullable type! Note that out/return parameters will not have updated values until the data stream completes \\(after the 'foreach' for Query\\(\\.\\.\\., buffered: false\\), or after the GridReader has been disposed for QueryMultiple\\)","errorType":"exception","errorClass":"ApplicationException","httpStatus":null,"severity":"error","filePath":"Dapper/DynamicParameters.cs","lineNumber":315,"sourceCode":"        /// </summary>\r\n        public IEnumerable<string> ParameterNames => parameters.Select(p => p.Key);\r\n\r\n        /// <summary>\r\n        /// Get the value of a parameter\r\n        /// </summary>\r\n        /// <typeparam name=\"T\"></typeparam>\r\n        /// <param name=\"name\"></param>\r\n        /// <returns>The value, note DBNull.Value is not returned, instead the value is returned as null</returns>\r\n        public T Get<T>(string name)\r\n        {\r\n            var paramInfo = parameters[Clean(name)];\r\n            var attachedParam = paramInfo.AttachedParam;\r\n            object? val = attachedParam is null ? paramInfo.Value : attachedParam.Value;\r\n            if (val == DBNull.Value)\r\n            {\r\n                if (default(T) is not null)\r\n                {\r\n                    throw new ApplicationException(\"Attempting to cast a DBNull to a non nullable type! Note that out/return parameters will not have updated values until the data stream completes (after the 'foreach' for Query(..., buffered: false), or after the GridReader has been disposed for QueryMultiple)\");\r\n                }\r\n                return default!;\r\n            }\r\n            return (T)val!;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Allows you to automatically populate a target property/field from output parameters. It actually\r\n        /// creates an InputOutput parameter, so you can still pass data in.\r\n        /// </summary>\r\n        /// <typeparam name=\"T\"></typeparam>\r\n        /// <param name=\"target\">The object whose property/field you wish to populate.</param>\r\n        /// <param name=\"expression\">A MemberExpression targeting a property/field of the target (or descendant thereof.)</param>\r\n        /// <param name=\"dbType\"></param>\r\n        /// <param name=\"size\">The size to set on the parameter. Defaults to 0, or DbString.DefaultLength in case of strings.</param>\r\n        /// <returns>The DynamicParameters instance</returns>\r\n        public DynamicParameters Output<T>(T target, Expression<Func<T, object?>> expression, DbType? dbType = null, int? size = null)\r\n        {\r","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/DapperLib/Dapper/blob/72a54c475f75e18cb93cba0809d00a5e6e49efd9/Dapper/DynamicParameters.cs#L297-L333","documentation":"DynamicParameters.Get<T> throws ApplicationException when the output/return parameter value is DBNull.Value and T is a non-nullable value type (default(T) is not null). The library cannot return a null for a value type and refuses to silently coerce DBNull into a default. The message also warns that out/return values are not populated until the data stream completes (after the unbuffered Query foreach, or after the GridReader is disposed for QueryMultiple).","triggerScenarios":"Calling param.Get<int>(\"@count\") where the procedure returned NULL for that output parameter; reading an output parameter too early (before the unbuffered result stream or GridReader finished); mapping a database NULL to a non-nullable type T.","commonSituations":"Stored procedure returning NULL on an error/no-rows path bound to int/long/Guid parameters; calling Get<T> inside an unbuffered foreach before the reader finished; mismatch between an always-nullable DB column and a non-nullable CLR type.","solutions":["Use a nullable type T (int? / long?) so DBNull maps to null instead of throwing.","Ensure the data stream is fully consumed before reading output parameters (dispose the GridReader, finish the unbuffered foreach).","Coalesce NULL to a default in the SQL procedure (ISNULL/COALESCE) when a non-nullable result is contractually expected."],"exampleFix":"// before\nint count = p.Get<int>(\"@count\"); // throws if proc returned NULL\n\n// after\nint? count = p.Get<int?>(\"@count\");\nint safe = count ?? 0;","handlingStrategy":"type-guard","validationCode":"// use nullable T when the output parameter may be NULL\nint? count = p.Get<int?>(\"@count\");\nint safe = count ?? 0;","typeGuard":"static bool IsNullableType<T>() => !default(T).GetType().IsValueType || Nullable.GetUnderlyingType(typeof(T)) is not null;","tryCatchPattern":"try { return p.Get<T>(name); }\ncatch (ApplicationException ex) when (ex.Message.Contains(\"DBNull\"))\n{ /* T is a non-nullable value type and DB returned NULL — switch to nullable */ }","preventionTips":["Bind nullable DB columns to nullable CLR types (int?, DateTime?).","For unbuffered/QueryMultiple, dispose the reader / finish iterating before reading output params."],"tags":["dynamic-parameters","nullable","dbnull","output-parameters"],"backgroundTag":null,"analyzedSha":"72a54c475f75e18cb93cba0809d00a5e6e49efd9","analyzedAt":"2026-08-13T14:18:18.115Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}