【问题标题】:.NET SqlParameter constructor inconsistent?.NET SqlParameter 构造函数不一致?
【发布时间】:2014-02-19 04:39:23
【问题描述】:

谁能告诉我这个函数发生了什么? 在下面的代码 sn-p 中,user.Id = 0id.Value = 0id.SqlDbType = Int.. 符合预期,因为 user.Id 是一个 int 字段。

但是,error.Value = nullerror.SqlDbType = BigInt。是什么赋予了?如果我使用非零,它会检测到一个 int 和正确的值。

注意:声明参数方向前后的Value属性是一样的。

public static long InsertUpdate(User user) {

    SqlParameter id = new SqlParameter("@id", user.Id);
    id.Direction = ParameterDirection.InputOutput;
    cmd.Parameters.Add(id);

    SqlParameter error = new SqlParameter("@error_code", 0);
    error.Direction = ParameterDirection.Output;
    cmd.Parameters.Add(error);

    .... other stuff
}

同样,如果存储过程中的@SET @error_Code = 0,则在过程运行后error.Value = NULL 和error.SqlDbType = NVarChar。如果我将它设置为一个整数,我会得到一个 Int 类型。

更新: 指定 SqlDbType.Int 后,参数现在在命令前后都有正确的 SqlDbType ......但是,当我实际上将其设置为 0 时,存储过程仍在设置 @error_code = null。

更新: 当 sproc 执行 SELECT 语句时,@error_code 参数始终返回为 null,无论它何时被设置...这仅在有 select 语句时发生...

这里是重现的过程:

ALTER PROCEDURE [dbo].[usp_user_insert_v1]
    @username VARCHAR(255),
    @password VARCHAR(255),
    @gender CHAR(1),
    @birthday DATETIME,
    @error_code INT OUTPUT
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @default_dt DATETIME
    EXEC @default_dt = uf_get_default_date
    DECLARE @dt DATETIME = GETUTCDATE()

            INSERT INTO users(username, password, gender, birthday, create_dt, last_login_dt, update_dt, deleted)
            VALUES(@username, @password, @gender, @birthday, @dt, @default_dt, @default_dt, 0)
            SELECT * FROM users WHERE id = SCOPE_IDENTITY()
            SET @error_code = 3
            RETURN

END

解决方案?

http://forums.asp.net/t/1208409.aspx?Interesting+problem+with+getting+OUTPUT+parameters+from+SQL+Server+using+C+

在 ASP 论坛上找到此链接...显然,在您从 SqlDataReader 读取所有结果之前,您无法读取输出参数...对我来说非常不幸,因为我决定是否要阅读结果基于输出参数...

【问题讨论】:

    标签: sql .net stored-procedures sqlparameter


    【解决方案1】:

    来自SqlParameter.Value on MSDN

    对于输出和返回值参数,在SqlCommand完成时设置值

    即我不会依赖类型推断来隐式设置返回类型。 我会明确设置输出参数的类型:

    var error = new SqlParameter("@error_code", SqlDbType.Int)
    {
        Direction = ParameterDirection.Output
    };
    

    编辑

    经过SqlParameter的一些反思:

    BigInt 很容易解释 - 它是默认的 SqlDbTypeSqlParameter(string parameterName, object value) ctor 不会覆盖此值。

    public enum SqlDbType
    {
      BigInt = 0,
      ...
    

    回复:@error_code 返回为 NULL

    我唯一能想到的是 PROC 无法干净地完成。尝试将SET @error_code = 0 移到EXEC @default_dt = uf_get_default_date 上方?

    编辑

    确认,@Damien 的观点是正确的

    SqlParameter error = new SqlParameter("@error_code", 0);
    

    其实叫这个ctor:

    public SqlParameter(string parameterName, SqlDbType dbType)
    

    SqlParameter error = new SqlParameter("@error_code", 1234);
    

    来电

    public SqlParameter(string parameterName, object value)
    

    原因:0 is implicitly castable to enum.

    【讨论】:

    • 也许这应该是一个不同的问题,但是当我的 sql 过程声明“@error_code INT OUTPUT”并在程序的开始?在根据您的链接执行命令后,它似乎至少应该将其设置为正确的数据类型。
    • 要打开一个新问题,为什么我的输出参数在 sproc 中为空(我已经验证在过程中执行 SELECT 语句并且输出参数设置为 null 时会发生这种情况无论是 0 还是非零)。 Def +1 提醒我对象初始化器。
    • (string,object) 的构造函数根本没有被调用,对于 error
    • @Damien 干得好——确实, (string, object) ctor 没有被调用为零,而是被调用为任何其他 INT。 Wow.
    • 我喜欢你的其他答案,不过 +1。
    【解决方案2】:

    当前的两个答案都略微不正确,因为它们基于这样的假设:为您的 error 对象调用的构造函数是 (string,object) 之一。不是这种情况。文字0 可以转换为任何枚举类型1,并且这种转换比转换为object 更可取。所以被调用的构造函数就是(string,SqlDbType)构造函数。

    因此类型设置为BigInt,因为这是SqlDbType 枚举的0 值,而Value 为空,因为您没有尝试设置该值的代码。

    SqlParameter error = new SqlParameter("@error_code", (object)0);
    

    应该让它选择正确的重载。


    演示:

    using System;
    using System.Data;
    
    namespace ConsoleApplication
    {
        internal class Program
        {
            private static void Main()
            {
                var a = new ABC("ignore", 0);
                var b = new ABC("ignore", (object)0);
                var c = new ABC("ignore", 1);
                int i = 0;
                var d = new ABC("ignore", i);
                Console.ReadLine();
            }
    
        }
    
        public class ABC
        {
            public ABC(string ignore, object value)
            {
                Console.WriteLine("Object");
            }
    
            public ABC(string ignore, SqlDbType value)
            {
                Console.WriteLine("SqlDbType");
            }
        }
    }
    

    打印:

    SqlDbType
    Object
    Object
    Object
    

    1来自C# Language specification, version 5,第 1.10 节(即,只是在语言的介绍中,而不是深埋在语言律师位中):

    为了方便使用枚举类型的默认值,文字 0 隐式转换为任何枚举类型。因此,以下是允许的。

    Color c = 0;
    

    我也认为这很重要,可以在 MSDN 上的语言参考中找到,但还没有找到明确的来源。

    【讨论】:

    • 你能解释一下为什么 SqlParamter("@id", user.Id) 创建一个 Int 而不是 SqlParameter("@error_code", 0) 吗? user.Id 是一个设置为 0 的 int 字段。此外,SqlParamter("@error_code", 2) 正确创建将 SqlDataParamter 设置为 int ......所以我不认为它正在选择 SqlDataType(name, SqlDbType) 构造函数当传递一个整数文字时。
    • user.Id 在任何情况下都不是整数 literal。整数文字 0 具有这种特殊情况,允许将其转换为 enum 值。而且,如果您不相信我,请尝试在我的答案中包含的示例代码中添加更多案例,我这样做是为了让您查看针对不同情况选择了哪个重载。
    • 对不起,字面意思是完全错误的词。我明白你的意思,奇怪的是其他整数文字没有被解释为 SqlDbType 枚举。
    • @Cailen - 你看到我添加的更新了吗? - 这是该语言的一个特定功能,专门针对 0
    • 感谢您的更新!这解释了我的最后一个问题。
    【解决方案3】:

    嗯,看起来最可靠的方法是使用this overload

    SqlParameter error = new SqlParameter("@error_code", SqlDBType.Int);
    error.Value = 0;
    

    您使用的重载将object 作为参数,出于某种我无法预测的原因,它没有选择正确的类型。

    【讨论】:

    • 编译器更倾向于将0转换为枚举类型,而不是将其转换为object
    猜你喜欢
    • 1970-01-01
    • 2011-10-18
    • 2013-12-16
    • 1970-01-01
    • 2012-01-11
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2014-08-01
    相关资源
    最近更新 更多