【问题标题】:Using parameters with EntityFramework and `FromSql`将参数与 EntityFramework 和 `FromSql` 一起使用
【发布时间】:2017-07-31 13:12:56
【问题描述】:
public List<PostJobListModel> GetPostsByCompanyId(int id, int s, int d, int p)
{
    string command = @"select Id,Title,Cities = STUFF(
     (SELECT  ',' + City.Name  
      FROM City where City.Id in (select Id from LocaitonJobRelationship as ljr where ljr.JobId = PostJob.Id)
      FOR XML PATH ('')), 1, 1, ''),
      Features = STUFF(
     (SELECT  ',' + Feature.Name  
      FROM Feature where Feature.Id in (select FeatureId from FeatureJobRelationship as fjr where fjr.JobId = PostJob.Id and (fjr.CategoryId in (@s,@d,@p) ) )FOR XML PATH('')), 1, 1, '')from PostJob where CompanyId = " + id + "";

    SqlParameter parameterS = new SqlParameter("@s", s);
    SqlParameter parameterD = new SqlParameter("@d", d);
    SqlParameter parameterP = new SqlParameter("@p", p);

    return _repositoryCustom.FromSql(command, s, d, p).ToList();
}

//存储库

public List<PostJobListModel> FromSql(string sql, params object[] objects)
{
    return _context.PostJobListModel.FromSql(sql,objects).ToList();
}

这段代码给出了“SQLException 必须声明标量变量“@variableName”” 我如何创建安全命令字符串?

编辑答案return _repositoryCustom.FromSql(command, parameterS , parameterD , parameterP ).ToList();

【问题讨论】:

  • 调用FromSql时没有包含参数。如果只调用EF FromSql,使用SqlCommand的目的是什么?
  • 用于自定义 sql 查询。调用时如何包含参数?
  • 要么需要使用带参数的SqlCommand对象来执行语句,要么使用纯文本字符串调用.FromSql()。您混合了这两种方法,这就是您的参数消失的原因。添加参数不会像string.Parse 那样修改.CommandText。我还建议您将所有变量作为参数传递到您的命令中。将它们推送到字符串的中间会使您面临潜在的 SQL 注入攻击。
  • @AndrewS 谢谢你的评论,你能给我一个不只是安全的示例代码吗?
  • @AndrewS FromSql() 也接受参数,你只需要传入。

标签: .net entity-framework asp.net-core .net-core


【解决方案1】:

你不通过SqlCommand设置参数,你需要将参数传递给FromSql语句。来自the documention

您还可以构造一个 DbParameter 并将其作为参数提供 价值。这允许您在 SQL 查询中使用命名参数 字符串+

var user = new SqlParameter("user", "johndoe");

var blogs = context.Blogs
    .FromSql("EXECUTE dbo.GetMostPopularBlogsForUser @user", user)
    .ToList();

所以对于你的代码,你会这样做

public List<PostJobListModel> GetPostsByCompanyId(int id, int s, int d, int p)
{
    string command = @"select Id,Title,Cities = STUFF(
     (SELECT  ',' + City.Name  
      FROM City where City.Id in (select Id from LocaitonJobRelationship as ljr where ljr.JobId = PostJob.Id)
      FOR XML PATH ('')), 1, 1, ''),
      Features = STUFF(
     (SELECT  ',' + Feature.Name  
      FROM Feature where Feature.Id in (select FeatureId from FeatureJobRelationship as fjr where fjr.JobId = PostJob.Id and (fjr.CategoryId in (@s,@d,@p) ) )FOR XML PATH('')), 1, 1, '')from PostJob where CompanyId = " + id + "";

    SqlParameter parameterS = new SqlParameter("@s", s);
    SqlParameter parameterD = new SqlParameter("@d", d);
    SqlParameter parameterP = new SqlParameter("@p", p);

    return _repositoryCustom.FromSql(command, parameterS, parameterD, parameterP).ToList();
}

您还应该将id 也设为参数。

【讨论】:

  • 同样的错误:System.Data.SqlClient.SqlException:'必须声明标量变量“@s”。'并为问题编辑/添加了新信息
  • @OMansAK 您需要调用.FromSql(command, parameterS, parameterD, parameterP),在您更新的示例中,您执行了.FromSql(command, s, d, p),这就是您仍然收到错误的原因。我更新了我的答案以使用您的新代码示例。
  • 噢,对不起我的错误。谢谢
  • 我们如何将 List 传递给 proc 或用户定义的 dataType 表 [dbo].[StringList]?
【解决方案2】:

当我通过 EF Core 调用 SQL 存储过程时,我喜欢构建一个 SqlParameter 数组来帮助我将 SQL 与 FromSql 调用中所需的参数混合在一起,如下所示:

using (var scope = _services.CreateScope())
{
    var parameters = new []
    {
        new SqlParameter("param1", System.Data.SqlDbType.VarChar, 10) { Value = someInboundMethodParam },
        new SqlParameter("param2", System.Data.SqlDbType.VarChar, 50) { Value = Environment.UserName },

        // etc..
    };

    var sql = new System.Text.StringBuilder("exec YourStoreProc");

    sql.Append(string.Join(separator: ",", values: parameters.Select(p => $" @{ p.ParameterName }")));

    var ctx = scope.ServiceProvider.GetRequiredService<YourContext>();

    return await ctx
        .Query<SomeView>()
        .AsNoTracking()
        .FromSql(sql.ToString(), parameters.ToArray<object>())
        .FirstOrDefaultAsync(cancellationToken);
}

经过一些迭代,我构建了以下内容来帮助我编写 SQL:

public static string GetStoredProcSql(string name, SqlParameter[] parameters)
{
    var stringOfParameters =
        string.Join(
            separator: ",",
            values: parameters.Select(GetStringOfParameterNameValuePairs)
        );
    return $"exec [{name}] {stringOfParameters};";
}

public static string GetStringOfParameterNameValuePairs(SqlParameter parameter)
{
    var name = parameter.ParameterName;
    var value =
        ShouldSqlDbTypeValueRequireQuotes(parameter)
            ? $"\'{parameter.Value}\'"
            : parameter.Value;
    return $" @{name}={value}";
}

public static bool ShouldSqlDbTypeValueRequireQuotes(SqlParameter parameter)
{
    return new Enum[] {
        // SqlDbTypes that require their values be wrapped in single quotes:
        SqlDbType.VarChar,
        SqlDbType.NVarChar
    }.Contains(parameter.SqlDbType);
}

并使用:

public async Task<List<JobView>> GetJobList(
    CancellationToken cancellationToken,
    int pageStart = 1,
    int pageSize = 10
)
{
    using (var scope = _services.CreateScope())
    {
        var logger = scope.ServiceProvider.GetRequiredService<ILogger<ISomeService>>();

        logger.LogInformation($"Getting list of share creation jobs..");

        var ctxJob = scope.ServiceProvider.GetRequiredService<JobContext>();

        var sql = SqlDataHelper.GetStoredProcSql(  // use here!
            "GetJobList",
            new[]
            {
                new SqlParameter("pageStart", System.Data.SqlDbType.Int) { Value = pageStart },
                new SqlParameter("pageSize", System.Data.SqlDbType.Int) { Value = pageSize },
                //new SqlParameter("filterFieldValuePairs", System.Data.SqlDbType.VarChar) { Value = filter },
                new SqlParameter("itemType", System.Data.SqlDbType.VarChar, 10) { Value = "Share" },
            });

        return await ctxJob
            .Query<JobView>()
            .AsNoTracking()
            .FromSql(sql)
            .ToListAsync(cancellationToken);
    }
}

【讨论】:

  • 这对动态生成的SQL很有帮助!
【解决方案3】:

在 .Net Core 2.0+ 中,下面的代码对我有用。无需通过 SQLParameter 类。

public List<XXSearchResult> SearchXXArticles(string searchTerm,bool published=true,bool isXX=true)
    {

        var listXXArticles = _dbContext.XXSearchResults.FromSql($"SELECT * FROM [dbo].[udf_textContentSearch] ({searchTerm}, {published}, {isXX})").ToList();
        return listXXArticles;
    }

// XXSearchResult 在 DbContext 类中声明为: 公共 DbQuery XXSearchResults { 获取;放; }

// udf_textContentSearch 是我在 SQL Server 数据库中的遗留函数。 SELECT * FROM [dbo].[udf_textContentSearch]

【讨论】:

  • 但不保护 sql 注入
  • @OMansAK 是的。 FromSqlRawSqlString 作为参数。只要您使用插值字符串作为输入(如 OP 建议的那样),参数就会被 SQL 转义
猜你喜欢
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-13
相关资源
最近更新 更多