3 回答
TA贡献1848条经验 获得超2个赞
Sql 注入这个大问题的一部分,你正面临着一个决定。如果您要插入的元素很少,那么您可能可以使用@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 扩展命令,您可以在其中直接传递要插入的项目列表和命令文本。您只需要让参数名称与列表中的属性名称相匹配。
TA贡献1865条经验 获得超7个赞
我强烈建议使用准备好的语句。这将一方面消除每次解析查询的一些不必要的开销,另一方面它会迫使您使用参数化查询,这将防止类型转换问题——我认为这会发生在您的代码中,因为url大多数可能是某种字符类型,而您没有添加引号——以及 SQL 注入。
string query = "INSERT INTO table(CampaignId, CookieId, Url) VALUES (@campaignid, @cookieid, @url)";
using (SqlConnection c = new SqlConnection(connectstring)) {
c.Open();
SqlCommand cmd = new SqlCommand(query, c);
cmd.Parameters.Add(new SqlParameter("@campaignid", SqlDbType.Int, 0)); //use appropriate type/size here
cmd.Parameters.Add(new SqlParameter("@cookieid", SqlDbType.Int, 0)); //use appropriate type/size here
cmd.Parameters.Add(new SqlParameter("@url", SqlDbType.NVarChar, 500)); //use appropriate type/size here
cmd.Prepare();
foreach (var item in container.items) {
cmd.Parameters[0].value = item.CampaignId;
cmd.Parameters[1].value = item.VisitorExternalId;
cmd.Parameters[2].value = item.url;
cmd.ExecuteNonQuery();
}
}
TA贡献2080条经验 获得超4个赞
如果您只想对数据库进行一次调用以执行所有插入操作,那么一种选择是使用带有列表的存储过程:
用于创建将存储我们的列表的类型的 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);
}
}
}
}
您可以将创建数据表的代码从您的类型中提取到帮助程序中以使其更小。
优点:参数化正确。我更喜欢调用存储过程而不是对数据库运行 sql(但您可能对此有不同的看法。)
结果调用
await InsertCampigns();
广告系列 ID | CookieId | 网址
1 | 1 | 酒吧
2 | 2 | 富
要在没有存储过程的情况下执行此操作,请参阅@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
- 3 回答
- 0 关注
- 175 浏览
添加回答
举报