You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms经用户定义表类型插入含varbinary字段DataTable至SQL Server失败

问题核心原因

你WinForm直连数据库能成功,是因为直接把内存中原生、类型完全匹配的DataTable传给SqlClient,不存在序列化/反序列化的类型转换问题。走Web API接口失败的核心原因有两个:

  1. 直接传DataTable作为接口入参兼容性极差:你在Xamarin端把DataTable序列化成JSON时,byte[]类型的profilePic会被转成Base64字符串,Web API端默认反序列化DataTable时,不会自动把这个Base64字符串转回byte[],最终反序列化出来的DataTable里profilePic列是字符串类型,和SQL要求的varbinary类型不匹配,自然插入失败。
  2. 你之前给结构化参数没指定TypeName,如果列顺序/类型和数据库自定义表类型不完全对齐,也会触发插入错误。你去掉profilePic字段后剩下的都是字符串类型,反序列化不会出错,所以能跑通。
修复方案

按以下步骤修改即可解决问题:

  • 第一步:废弃直接传DataTable的做法,定义强类型传输对象,从根源避免序列化类型不匹配问题
public class StdDetailDto
{
    public string id { get; set; }
    public string section { get; set; }
    public byte[] profilePic { get; set; }
}
  • 第二步:修改Xamarin端发送逻辑,不要序列化DataTable,直接传强类型列表
async private void hf_Clicked(object sender, EventArgs e)
{
    urlClass urldata = new urlClass();
    string uri = urldata.url + "/PostStdDetails";
    byte[] bytess = new byte[] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 };

    // 替换原来构造DataTable的逻辑,直接构造强类型列表
    var postData = new List<StdDetailDto>
    {
        new StdDetailDto{ id = "STDID0010", section = "A", profilePic = bytess },
        new StdDetailDto{ id = "STDID0011", section = "B", profilePic = bytess }
    };

    StringContent content = new StringContent(JsonConvert.SerializeObject(postData), Encoding.UTF8, "application/json");

    try
    {
        HttpResponseMessage responsepost = await client.PostAsync(uri, content);
        if (responsepost.IsSuccessStatusCode == true)
        {
            await DisplayAlert("Section Added", "Section per Grade is added successfully", "OK");
        }
        else
        {
            // 读取接口返回的具体错误内容,方便排查问题
            var errMsg = await responsepost.Content.ReadAsStringAsync();
            await DisplayAlert("Operation Failed", $"Response Failed! {errMsg}", "Cancel");
        }
    }
    catch (System.Net.WebException exp)
    {
        bool ans = await DisplayAlert("Connection Failed", "Please Check Your Internet Connection!", "Retry", "Cancel");
        if (ans == true)
            hf_Clicked(sender, e);
    }
    catch (Exception exp)
    {
        bool ans = await DisplayAlert("Connection Failed", "Lost Connection!", "Retry", "Cancel");
        if (ans == true)
            hf_Clicked(sender, e);
    }
}
  • 第三步:修改Web API端的Post方法,入参接收强类型列表,手动在服务端构造和数据库自定义表类型完全匹配的DataTable,结构化参数必须指定TypeName
public void PostStdDetails([FromBody] List<StdDetailDto> stddetails)
{
    // 手动构造DataTable,列顺序、列名、列类型必须和数据库中dbo.StdDetailsTypeTable定义完全一致
    DataTable dt = new DataTable();
    dt.Columns.Add("id", typeof(string));
    dt.Columns.Add("section", typeof(string));
    dt.Columns.Add("profilePic", typeof(byte[]));

    foreach (var item in stddetails)
    {
        dt.Rows.Add(item.id, item.section, item.profilePic);
    }

    // 用using自动释放资源,不需要手动写Close
    using (SqlConnection conn = new SqlConnection(DBConnection))
    {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand("getstddetails", conn))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            SqlParameter tvpParam = cmd.Parameters.Add("@stdDetailsTable", SqlDbType.Structured);
            // 必须指定TypeName,和数据库自定义类型完全对应
            tvpParam.TypeName = "dbo.StdDetailsTypeTable";
            tvpParam.Value = dt;
            cmd.ExecuteNonQuery();
        }
    }
}
  • 第四步:给Web API方法加异常捕获和日志,不要只返回通用失败信息,把具体异常信息记录下来,后续排错不需要反复试错。

注意:你贴的WinForm测试代码里第三列名写的是profile,和自定义表类型的profilePic不一致,能跑通应该是贴代码时的笔误,实际代码里列名、列顺序必须和数据库定义完全一致,差一个字符都可能报错。

内容的提问来源于stack exchange,提问作者rana hd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 06:54:28