diff --git a/Dapper/DynamicParameters.cs b/Dapper/DynamicParameters.cs index f6708b5ce..e0f707b96 100644 --- a/Dapper/DynamicParameters.cs +++ b/Dapper/DynamicParameters.cs @@ -305,7 +305,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) /// /// All the names of the param in the bag, use Get to yank them out /// - public IEnumerable ParameterNames => parameters.Select(p => p.Key); + public IEnumerable ParameterNames => GetParamNames(); /// /// Get the value of a parameter @@ -315,18 +315,41 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) /// The value, note DBNull.Value is not returned, instead the value is returned as null public T Get(string name) { - var paramInfo = parameters[Clean(name)]; - var attachedParam = paramInfo.AttachedParam; - object? val = attachedParam is null ? paramInfo.Value : attachedParam.Value; - if (val == DBNull.Value) + var cleanName = Clean(name); + if (parameters.TryGetValue(cleanName, out var paramInfo)) { - if (default(T) is not null) + object? val = paramInfo!.AttachedParam is null ? paramInfo.Value : paramInfo.AttachedParam.Value; + if (val == DBNull.Value) { - 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)"); + if (default(T) is not null) + { + 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)"); + } + return default!; } - return default!; + return (T)val!; } - return (T)val!; + if (templates != null) + { + var foundValue = templates + .Select(obj => obj?.GetType().GetProperty(cleanName)?.GetValue(obj)) + .FirstOrDefault(value => value != null); + + if (foundValue != null) + { + if (foundValue == DBNull.Value) + { + if (default(T) is not null) + { + 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)"); + } + return default!; + } + return (T)foundValue; + } + } + + throw new KeyNotFoundException($"Key {name} could not be found in param list."); } /// @@ -496,5 +519,18 @@ void SqlMapper.IParameterCallbacks.OnCompleted() param.OutputCallback?.Invoke(param.OutputTarget, this); } } + private List GetParamNames() + { + var paramBagItems = parameters.Select(p => p.Key); + var propNames = new List(); + if(templates != null) + { + var templateProps = templates.SelectMany(obj => obj.GetType().GetProperties()); + propNames.AddRange(templateProps.Select(prop => prop.Name).Distinct()); + } + + propNames.AddRange(paramBagItems); + return propNames; + } } } diff --git a/tests/Dapper.Tests/ParameterTests.cs b/tests/Dapper.Tests/ParameterTests.cs index 650624c20..61dde978b 100644 --- a/tests/Dapper.Tests/ParameterTests.cs +++ b/tests/Dapper.Tests/ParameterTests.cs @@ -376,6 +376,68 @@ public void TestTVP() } } } + [Fact] + public void TestGetSqlBuilderParamNames() + { + var sqlBuilder = new SqlBuilder(); + + var templateA = sqlBuilder.AddTemplate("SELECT @a", new + { + a = 1 + }); + var templateB = sqlBuilder.AddTemplate("SELECT @b", new + { + b = 2 + }); + sqlBuilder.AddParameters(new + { + c = 3 + }); + + var aParameters = new DynamicParameters(templateA.Parameters); + var aParamNames = aParameters.ParameterNames; + + var bParameters = new DynamicParameters(templateB.Parameters); + var bParamNames = bParameters.ParameterNames; + + Assert.Contains("a", aParamNames); + Assert.DoesNotContain("b", aParamNames); + Assert.Contains("c", aParamNames); + Assert.DoesNotContain("a", bParamNames); + Assert.Contains("b", bParamNames); + Assert.Contains("c", bParamNames); + } + [Fact] + public void TestGetSqlBuilderParams() + { + var sqlBuilder = new SqlBuilder(); + + var templateA = sqlBuilder.AddTemplate("SELECT @a", new + { + a = 1 + }); + var templateB = sqlBuilder.AddTemplate("SELECT @b", new + { + b = 2 + }); + sqlBuilder.AddParameters(new + { + c = 3 + }); + + var aParameters = new DynamicParameters(templateA.Parameters); + var bParameters = new DynamicParameters(templateB.Parameters); + + var aParamValue = aParameters.Get("a"); + var bParamValue = bParameters.Get("b"); + var cParamValueA = aParameters.Get("c"); + var cParamValueB = bParameters.Get("c"); + + Assert.Equal(1, aParamValue); + Assert.Equal(2, bParamValue); + Assert.Equal(3, cParamValueA); + Assert.Equal(3, cParamValueB); + } private class DynamicParameterWithIntTVP : DynamicParameters, SqlMapper.IDynamicParameters {