C#替换foreach

时间:2018-08-09 10:22:28

标签: c#

我正在使用Webservice GET请求来获取有关客户的一些信息。我想为从请求返回的每个客户向DB插入一条记录。

我现在有以下代码:

            var container = JsonConvert.DeserializeObject<MarkedCampaigns>(json);
            string insertDB = "";
            foreach (var item in container.items)
            {
                insertDB += "INSERT INTO TABLE (CampaignId,CookieId,Url) values(" + item.CampaignId + "," + item.VisitorExternalId + "," + item.Url + ");";
            }
            //EXECUTE STRING .

容器是Get请求的响应。它包含一个Item属性,每个项目代表一个客户。

我的问题是,这是将记录插入数据库的正确方法吗?还是有使用容器的更简单方法以及一些我不熟悉的方法?

3 个答案:

答案 0 :(得分:3)

我强烈建议使用准备好的陈述。一方面,这将消除每次解析查询的不必要的开销,另一方面,它将迫使您使用参数化查询,这将避免类型转换的问题-我认为这会在您的代码中发生,因为{{ 1}}很可能是某些字符类型,您没有添加引号和SQL注入。

url

答案 1 :(得分:3)

如果您只想对数据库进行一次调用以进行所有插入操作,那么一种选择是使用带有列表的存储过程:

用于创建将存储我们的列表的类型的sql:

create type [dbo].CampaignList as table (CampaignId int,  CookieId int, [Url] varchar(255))

执行插入操作的存储过程

create procedure [dbo].[spSaveCampaigns]
    @CampaignList CampaignList readonly
as
    insert into tblCampaigns (CampaignId, CookieId, [Url])
    select CampaignId, CookieId, [Url] from @CampaignList;

调用它的C#:

public async Task InsertCampigns()
        {
            var campaigns = new List<Campaign> {new Campaign(1, 1, "bar"), new Campaign(2, 2, "foo") };
            using (var sqlConnection = new SqlConnection(_connectionString))
            {
                using (var cmd = new SqlCommand("exec [dbo].[spSaveCampaigns] @CampaignList", sqlConnection))
                {
                    await sqlConnection.OpenAsync().ConfigureAwait(false);

                    using (var table = new DataTable())
                    {
                        table.Columns.Add("CampaignId", typeof(int));
                        table.Columns.Add("CookieId", typeof(int));
                        table.Columns.Add("Url", typeof(string));

                        foreach (var campaign in campaigns)
                            table.Rows.Add(campaign.CampaignId, campaign.CookieId, $"{campaign.Url}");

                        var parameters = new SqlParameter("@CampaignList", SqlDbType.Structured)
                                         {
                                             TypeName = "dbo.CampaignList",
                                             Value = table
                                         };

                        cmd.Parameters.Add(parameters);
                        await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
                    }
                }
            }
        }

您可以将用于创建数据表的代码从您的类型中拉出到帮助器中,以缩小该数据表。

优点: 正确设置了参数。 我更喜欢调用存储的proc,而不是针对db运行sql(但您对此可能会有不同的看法。)

结果调用

await InsertCampigns();

CampaignId | CookieId |网址

1 | 1 |酒吧

2 | 2 | foo

要在没有存储过程的情况下执行此操作,请在评论中查看@Magnus的链接

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/table-valued-parameters#passing-a-table-valued-parameter-to-a-parameterized-sql-statement

答案 2 :(得分:2)

作为Sql Injection的一个大问题的一部分,您面临着要做出的决定。如果您要插入的元素很少,则可能可以使用一个简单的循环,如@derpisher的答案中所述,但是该答案需要为查询的每个元素调用数据库引擎,并且您需要定义自己的所有参数。

具有多个插入内容的单个命令文本是更可取的,因为您对数据库引擎进行了一次调用,并且在要插入多个记录的情况下,差异很明显。但是,如果要使用参数化查询,则后一种方法比较困难,因为对于每个要插入的值,您都需要一个参数。

相反,我建议您尝试使用Dapper

使用这个简单的ORM库,您可以编写

using(IDbConnection cnn = GetSqlConnection())
{
    string cmdText = @"INSERT INTO TABLE (CampaignId,CookieId,Url)
                       VALUES(@CampaignId, @VisitorExternalId, @Url)";
    cnn.Execute(cmdText, container.items);
}

这里,GetSqlConnection是一种方法,它返回已打开的连接的实例。 cmdText是要执行的命令,就好像您只有一条记录要插入一样。诀窍是Dapper添加的Execute extension命令,您可以在其中直接传递要插入的项目列表和命令文本。您只需要使参数名称与列表内的属性名称匹配即可。