简体   繁体   English

如何通过列表<Int>到 SQL Server 存储过程

[英]How to pass List<Int> to SQL Server stored procedure

I have this user table type in SQL Server:我在 SQL Server 中有这个用户表类型:

CREATE TYPE [dbo].[ListNew] AS TABLE
                               (
                                    [Id] [int] NOT NULL,
                                    PRIMARY KEY CLUSTERED ([Id] ASC) WITH (IGNORE_DUP_KEY = OFF)
                               )
GO

And use this type in stored procedure parameter:并在存储过程参数中使用此类型:

....
    (@lstNew ListNew READONLY,
     @UserName nvarchar(128))
AS
....

And using this stored procedure in ASP.NET MVC with this code:并在 ASP.NET MVC 中使用此存储过程和以下代码:

List<int> lstNew = MyList.Select(o => o.Key).ToList();
List<XXXView> lstView = db.Database.SqlQuery<XXXView>("MyStoredProcedure @lstNew,@UserName",
              new SqlParameter("@lstNew", lstNew),
              new SqlParameter("@UserName", userName)).ToList();

but it's not working and get this error:但它不起作用并出现此错误:

No mapping exists from object type System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] to a known managed provider native type.不存在从对象类型 System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 到已知托管提供程序本机类型的映射。

I try without ListNew and used only username, it's working我尝试不使用 ListNew 并且只使用用户名,它正在工作


Edit:编辑:

I use this code:我使用这段代码:

myParameter.SqlDbType = SqlDbType.Udt;
myParameter.UdtTypeName = "ListNew";

But I get the same warning但我得到同样的警告

This is a solved problem and properly documented in - cough - the documentation.这是一个已解决的问题,并已正确记录在 - 咳嗽 - 文档中。

YOu will need to define the table on the server side and then can pass in a table valued parameter.您将需要在服务器端定义表,然后可以传入表值参数。

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/table-valued-parameters https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/table-valued-parameters

This runs down to using the SqlDbType Structured.这归结为使用结构化的 SqlDbType。

// Configure the command and parameter.  
SqlCommand insertCommand = new SqlCommand(sqlInsert, connection);  
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue("@tvpNewCategories", addedCategories);  
tvpParam.SqlDbType = SqlDbType.Structured;  
tvpParam.TypeName = "dbo.CategoryTableType";

You CAN use a DataTable, but then you introduce the most overhead approach possible as object model - or you just use...您可以使用 DataTable,但随后您引入了尽可能多的开销方法作为对象模型 - 或者您只是使用...

https://forums.asp.net/t/1845039.aspx?Table+Value+Parameter+Use+With+C+without+using+DataTable https://forums.asp.net/t/1845039.aspx?Table+Value+Parameter+Use+With+C+without+using+DataTable

Basically you transform your data into SqlDataRecords and pass them in. Needs some metadata - but generally this can be generalized and fits in below a page of code.基本上,您将数据转换为 SqlDataRecords 并将它们传入。需要一些元数据 - 但通常这可以概括并适合下面的代码页。 The link has the code (which I can not copy here due to - well - it not being MY code).该链接有代码(我无法在此处复制,因为 - 好吧 - 它不是我的代码)。

I always use XML to pass these type of DATA to sqlserver sproc.我总是使用 XML 将这些类型的 DATA 传递给 sqlserver sproc。

When you pass your XML to sproc you can use something like this to have it as a table in your sproc:当您将 XML 传递给 sproc 时,您可以使用类似这样的方法将其作为 sproc 中的表:

(commented lines are my XML's structure. manipulate it for your own use.) (注释行是我的 XML 结构。操作它以供您自己使用。)

EXEC sp_xml_preparedocument @XmlHandle output,@FieldPermissionAccessXML
    --'<FieldPermissions>
    --<FieldPermission FieldName="" RoleID="1" Access="1" />
    --<FieldPermission FieldName="" RoleID="2" Access="1" />'
    --select * from JMP_FieldPermissions
INSERT INTO #FieldPermissionsTable 
SELECT *--ID,Value, Value2, Navi_User 
FROM  OPENXML (@XmlHandle, './Row_ID/Elements') 
      WITH (TE_ID VARCHAR(MAX) '@ID',
    Value VARCHAR(MAX) '@Value',
    Value2 VARCHAR(MAX) '@Value2',
    NAVI_USER VARCHAR(MAX) '@Navi_User'     
            )

Create the DataTable variable in c#, put the data in the DataTable, pass it to sp:在c#中创建DataTable变量,把数据放到DataTable中,传给sp:

List<int> lstNew = MyList.Select(o => o.Key).ToList();
DataTable lstNewTable = new ListNew();
foreach (var id in lstNew )
{
    lstNewTable.Rows.Add(id);
}

List<XXXView> lstView = db.Database.SqlQuery<XXXView>("MyStoredProcedure @lstNew,@UserName",new SqlParameter("@lstNew", lstNewTable),
              new SqlParameter("@UserName", userName)).ToList();

Several answers were close but none gave a full working model.几个答案很接近,但没有一个给出完整的工作模型。 @Alison Niu just needs to add a column & name to populate the DataTable. @Alison Niu 只需要添加一个列和名称来填充数据表。 @Saurabh Gadani the reflection is very flexible but the Props are Null. @Saurabh Gadani 反射非常灵活,但道具为空。 Also the cmd.Parameters needs some special values set for Sql to find the table definition.此外 cmd.Parameters 需要为 Sql 设置一些特殊值来查找表定义。 I am grateful for these answers that got me closer to a solution.我很感激这些让我更接近解决方案的答案。

Populate a DataTable from a List of Integers从整数列表中填充数据表

    private DataTable ListInt_ToTable(List<int> tableThese)
    {
        DataTable lstNewTable = new DataTable();
        //Columns
        lstNewTable.Columns.Add("ID", typeof(int));

        //Values
        foreach (var id in tableThese)
        {
            lstNewTable.Rows.Add(id);   
        }
        return lstNewTable;
    }

The TYPE defines the table layout for SQL to receive the parameter. TYPE 定义了 SQL 接收参数的表布局。 It can be written within the Stored Procedure but this is how I prefer to keep it in the application that built the data:它可以写在存储过程中,但这是我更喜欢将它保存在构建数据的应用程序中的方式:

public static string SqlCmd_spStackOverflow_Loaded_ListIDs = @"
            IF TYPE_ID(N'IdLoadedTableType') IS NULL
                Begin
                    CREATE TYPE dbo.IdLoadedTableType
                       AS TABLE ( ID INT );
                End

            EXEC dbo.spStackOverflow_Loaded_ListIDs
                   @TblIds
    ";

Sending the Sql to run the Stored Procedure.发送 Sql 以运行存储过程。 Notice the parameters **注意参数**

DataTable mappingTbl = ListInt_ToTable(mappingData);
using (SqlConnection sqlConnection = new SqlConnection(ConnectionString))
        {
            try
            {
                DataTable results = new DataTable();
                SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(SqlCmd_spStackOverflow_Loaded_ListIDs, sqlConnection);
                //apply parameters
                //NOT VIA sqlDataAdapter.SelectCommand.Parameters.AddWithValue(...);
                var specialParm = new SqlParameter();    // **
                specialParm.ParameterName = "@TblIds";   // acts like a Declare in SQL
                specialParm.Value = (object)mappingTbl;  // sets the data valaes                                                           
                specialParm.SqlDbType = SqlDbType.Structured; // unique to User-Defined Table parameters
                specialParm.TypeName = "dbo.IdLoadedTableType"; // refers to created type
                sqlDataAdapter.SelectCommand.Parameters.Add(specialParm); // done

                sqlDataAdapter.Fill(results);
                return results;
            }
            catch (Exception ex)
            {
                throw new Exception("An error occurred while executing a SQL Read statement with Parameters. SQL Statement: " + SqlCmd_spStackOverflow_Loaded_ListIDs + " Exception: " + ex.Message);
            }
        }

This model is working in my solution, except I use long in C# & BigInt in sql.这个模型在我的解决方案中工作,除了我在 C# 中使用 long 和在 sql 中使用 BigInt。 Please improve options as you find something.当你发现一些东西时,请改进选项。

You can not pass any generic type list as SP parameter, must have to pass it as DataTable instead of List.您不能将任何泛型类型列表作为 SP 参数传递,必须将其作为 DataTable 而不是 List 传递。 Here is an eaxample:这是一个示例:

DataTable mappingTbl = ListToDataTable(mappingData);

SqlConnection con = new SqlConnection(conStr);
            con.Open();
            SqlCommand cmd = new SqlCommand(StoredProcedure.GetSavedFormList, con);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@UDT_EBFromMappingTable", mappingTbl);


public DataTable ListToDataTable<T>(List<T> items)
        {
            DataTable dataTable = new DataTable(typeof(T).Name);

            //Get all the properties
            PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo prop in Props)
            {
                //Defining type of data column gives proper data table 
                var type = (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType);
                //Setting column names as Property names
                dataTable.Columns.Add(prop.Name, type);
            }

            foreach (T item in items)
            {
                var values = new object[Props.Length];
                for (int i = 0; i < Props.Length; i++)
                {
                    //inserting property values to datatable rows
                    values[i] = Props[i].GetValue(item, null);
                }
                dataTable.Rows.Add(values);
            }
            //put a breakpoint here and check datatable
            return dataTable;
        }

May this helps you.愿这对你有所帮助。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM