简体   繁体   English

Entity Framework Core 3.1 存储过程可选参数不起作用

[英]Entity Framework Core 3.1 stored procedure optional parameters not working

[SOLVED] (see the end) [已解决](见文末)

I've spent most of the day looking for a solution that works with no luck.我一天中的大部分时间都在寻找一个没有运气的解决方案。

  • EF Core 3.1英孚核心 3.1
  • Visual Studio 2019视觉工作室 2019
  • SQL Server 2017 SQL 服务器 2017
  • .Net Core 3.1 .Net 核心 3.1

I have not been able to find a solution that works to call a stored procedure from.Net Core 3.1 with optional parameters.我一直无法找到一种解决方案,该解决方案可以使用可选参数从 .Net Core 3.1 调用存储过程。 I've read dozens of articles from SO to blogs, but nothing works.我已经阅读了从 SO 到博客的数十篇文章,但没有任何效果。

Here's my C# code for the method to call the stored procedure with all the attempts at making this work:这是我的 C# 代码,用于调用存储过程的方法,并尝试进行此工作:

public List<BudgetWorkflowStatusByDepartment> GetBudgetWorkflowStatusByDepartment(int? companyId, int? departmentId, int? locationId, int? sublocationId)
{
        //var compId = new SqlParameter("@CompanyId", companyId);
        //compId.Value = (object)companyId ?? SqlInt32.Null;

        //var deptId = new SqlParameter("@DepartmentId", departmentId);
        //deptId.Value = (object)departmentId ?? SqlInt32.Null;

        //var locId = new SqlParameter("@LocationId", locationId);
        //locId.Value = (object)locationId ?? SqlInt32.Null;

        //var subId = new SqlParameter("@SubLocationId", sublocationId);
        //subId.Value = (object)sublocationId ?? SqlInt32.Null;

        var compId = new SqlParameter("@CompanyID", companyId);
        compId.Value = (object)companyId ?? DBNull.Value;

        var deptId = new SqlParameter("@DepartmentID", departmentId);
        deptId.Value = (object)departmentId ?? DBNull.Value;

        var locId = new SqlParameter("@LocationID", locationId);
        locId.Value = (object)locationId ?? DBNull.Value;

        var subId = new SqlParameter("@SubLocationID", sublocationId);
        subId.Value = (object)sublocationId ?? DBNull.Value;

        //var result = context.BudgetWorkflowStatusByDepartment
        //    .FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment @CompanyID, @DepartmentID, @LocationID, @SubLocationID", compId, deptId, locId, subId).ToList();

        //var result = context.BudgetWorkflowStatusByDepartment
        //    .FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment {0}, {1}, {2}, {3}", companyId, departmentId, locationId, companyId).ToList();

        // Error Must declare the scalar variable "@CompanyID".
        //var result = context.BudgetWorkflowStatusByDepartment
        //    .FromSqlRaw($"EXEC mis.BudgetWorkflowStatusByDepartment @CompanyID={compId}, @DepartmentID={deptId}, @LocationID={locId}, @SubLocationID={subId}").ToList();

        // Error Must declare the scalar variable "@CompanyID".
        var result = context.BudgetWorkflowStatusByDepartment
            .FromSqlRaw($"EXEC mis.BudgetWorkflowStatusByDepartment {compId}, {deptId}, {locId}, {subId}").ToList();

        //var result = context.BudgetWorkflowStatusByDepartment
        //        .FromSqlInterpolated($"EXEC mis.BudgetWorkflowStatusByDepartment @CompanyID={compId}, @DepartmentID={deptId}, @LocationID={locId}, @SubLocationID={subId}").ToList();

        return result;
}

None of these attempts work.这些尝试都不起作用。 The errors range from:错误范围包括:

Data is Null. This method or property cannot be called on Null values.数据为 Null。不能对 Null 值调用此方法或属性。

... to... ... 到...

Must declare the scalar variable "@compId".必须声明标量变量“@compId”。

... to some other errors when I just tried something that was most likely just incorrect in a desperate attempt to get it to work. ... 当我只是尝试一些很可能只是不正确的东西时,为了让它工作而绝望地尝试了一些其他错误。

This is the signature of the stored procedure itself showing that all parameters are nullable.这是存储过程本身的签名,表明所有参数都可以为空。

ALTER PROCEDURE [mis].[BudgetWorkflowStatusByDepartment] 
    (@CompanyID int = null,
     @DepartmentID int = null,
     @LocationID int = null,
     @SubLocationID int = null)

The frustrating part is that this stored procedure works, in SQL Server, in LinqPad with or without parameters.令人沮丧的是,这个存储过程在 SQL 服务器中工作,在 LinqPad 中有或没有参数。 And it works in my code only with ALL the parameters, but if any are null, it throws an error.它在我的代码中只适用于所有参数,但如果有任何参数是 null,它会抛出错误。

Any insight on how I can get the function to work with optional parameters, is appreciated.任何有关如何让 function 使用可选参数的见解,我都表示赞赏。

Edited: 8/24/2020编辑:2020 年 8 月 24 日

Based on the new suggestions, I tried them and for some reason they still don't work.根据新的建议,我尝试了它们,但由于某种原因它们仍然不起作用。

I've tested the sproc directly in SSMS with all four parameters, and it works.我已经使用所有四个参数直接在 SSMS 中测试了存储过程,它可以正常工作。 Then I removed each of the last params one by one and tested it as each was removed and it works returning correct data.然后我一个一个地删除了最后一个参数,并在每个参数被删除时对其进行了测试,并且它可以返回正确的数据。

When I do the same test using the Api and the service call to this method, the four params work, 3 params work, but 2 params, 1 param and no params, don't work.当我使用 Api 和对此方法的服务调用进行相同的测试时,4 个参数有效,3 个参数有效,但 2 个参数、1 个参数和无参数无效。 I don't understand why not.我不明白为什么不呢。

Here are my new attempts:这是我的新尝试:


            /// New attempts as of: 8/24/2020
            // Error Data is Null. This method or property cannot be called on Null values.
            //var compId = new SqlParameter("@CompanyID", companyId);
            //compId.Value = (object)companyId ?? DBNull.Value;

            //var deptId = new SqlParameter("@DepartmentID", departmentId);
            //deptId.Value = (object)departmentId ?? DBNull.Value;

            //var locId = new SqlParameter("@LocationID", locationId);
            //locId.Value = (object)locationId ?? DBNull.Value;

            //var subId = new SqlParameter("@SubLocationID", sublocationId);
            //subId.Value = (object)sublocationId ?? DBNull.Value;

            //var result = context.BudgetWorkflowStatusByDepartment
            //        .FromSqlInterpolated($"EXEC mis.BudgetWorkflowStatusByDepartment {compId}, {deptId}, {locId}, {subId}").ToList();

            // Error Data is Null. This method or property cannot be called on Null values.
            
            //var result = context.BudgetWorkflowStatusByDepartment
            //        .FromSqlInterpolated($"EXEC mis.BudgetWorkflowStatusByDepartment {companyId}, {departmentId}, {locationId}, {sublocationId}").ToList();


            //var compId = new SqlParameter("@CompanyID", companyId) { IsNullable = true };
            //compId.Value = (object)companyId ?? DBNull.Value;

            //var deptId = new SqlParameter("@DepartmentID", departmentId) { IsNullable = true };
            //deptId.Value = (object)departmentId ?? DBNull.Value;

            //var locId = new SqlParameter("@LocationID", locationId) { IsNullable = true };
            //locId.Value = (object)locationId ?? DBNull.Value;

            //var subId = new SqlParameter("@SubLocationID", sublocationId) { IsNullable = true };
            //subId.Value = (object)sublocationId ?? DBNull.Value;

            //// Error Data is Null. This method or property cannot be called on Null values.
            //var result = context.BudgetWorkflowStatusByDepartment
            //                    .FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment @CompanyID = @CompanyID, @DepartmentID = @DepartmentID, @LocationID = @LocationID, @SubLocationID = @SubLocationID", compId, deptId, locId, subId)
            //                    .ToList();

            var compId = new SqlParameter("@CompanyID", companyId) { IsNullable = true };
            compId.Value = (object)companyId ?? DBNull.Value;

            var deptId = new SqlParameter("@DepartmentID", departmentId) { IsNullable = true };
            deptId.Value = (object)departmentId ?? DBNull.Value;

            var locId = new SqlParameter("@LocationID", locationId) { IsNullable = true };
            locId.Value = (object)locationId ?? DBNull.Value;

            var subId = new SqlParameter("@SubLocationID", sublocationId) { IsNullable = true };
            subId.Value = (object)sublocationId ?? DBNull.Value;

            // Error Data is Null. This method or property cannot be called on Null values.
            var result = context.BudgetWorkflowStatusByDepartment
                                .FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment @CompanyID, @DepartmentID, @LocationID, @SubLocationID", compId, deptId, locId, subId)
                                .ToList();

I thought I should include my DbContext property settings for this sproc as additional info.我认为我应该将此存储过程的 DbContext 属性设置作为附加信息包括在内。 This same pattern is used for two other sprocs and they work but they don't require optional parameters.同样的模式用于其他两个存储过程,它们可以工作,但不需要可选参数。

        public DbSet<BudgetWorkflowStatusByDepartment> BudgetWorkflowStatusByDepartment { get; set; }

And in the OnModelCreating() method I set the HasNoKey() method.在 OnModelCreating() 方法中,我设置了HasNoKey()方法。

            modelBuilder.Entity<BudgetWorkflowStatusByDepartment>().HasNoKey();

I appreciate everyone's suggestions.我感谢大家的建议。

My work around on this will be to tell the DBA to change the sproc to remove the parameters all together, and I'll just do a .Where() clause on the result.我解决这个问题的方法是告诉 DBA 更改存储过程以一起删除所有参数,我将只对结果执行.Where()子句。

Luckily for this particular feature it won't be returning more than 200 rows, but it will return many columns, but this still should not be taxing.幸运的是,对于这个特殊功能,它不会返回超过 200 行,但会返回很多列,但这仍然不应该很费力。

Solved reason解决原因

It was my fault.这都是我的错。 I had some int properties that needed to be nullable ints.我有一些需要为可为空的整数的int属性。 Once I did that, it worked, and it seems to work with most solutions.一旦我这样做了,它就起作用了,而且它似乎适用于大多数解决方案。

It's important to pay attention to those properties that could be null and make them nullable, so you don't fall prey to my issue, that took an entire day to solve.重要的是要注意那些可能是 null 的属性并使它们可以为空,这样您就不会成为我的问题的牺牲品,我花了一整天的时间来解决这个问题。


I thought that since I was going through all these attempts at something I hadn't done before (working with optional params with.Net Core 3.1 stored procedures), that I'd document my results with the various options in handling this.我认为,既然我正在尝试所有这些我以前从未做过的事情(使用可选参数和 .Net Core 3.1 存储过程),我会用处理这个问题的各种选项记录我的结果。

These are some options on what works and what doesn't when executing a stored procedure from.Net Core 3.1.这些是关于从 .Net Core 3.1 执行存储过程时什么有效和什么无效的一些选项。

For these examples, this will be the signature of the.Net Core method.对于这些示例,这将是 .Net Core 方法的签名。

public List<BudgetWorkflowStatusByDepartment> GetBudgetWorkflowStatusByDepartment
(int? companyId, int? departmentId, int? locationId, int? sublocationId)
{
...
}

All of the parameters are optional.所有参数都是可选的。 The following examples would be the code you could use in this method.以下示例是您可以在此方法中使用的代码。

This is the signature for the stored procedure.这是存储过程的签名。

ALTER procedure mis.BudgetWorkflowStatusByDepartment_Filtered (
      @CompanyID int = null
    , @DepartmentID int = null
    , @LocationID int = null
    , @SubLocationID int = null
)

Working Examples工作实例

The following examples demonstrate how to use either the FromSqlRaw or FromSqlInterpolated methods to execute the stored procedure.以下示例演示如何使用FromSqlRawFromSqlInterpolated方法来执行存储过程。

FromSqlRaw Methods FromSqlRaw 方法

This example simply creates SqlParameter objects and sets the values to the incoming value, or DbNull .此示例仅创建SqlParameter对象并将值设置为传入值或DbNull This is important as null won't work in this case.这很重要,因为null在这种情况下不起作用。 (See Non Working Examples below) (参见下面的非工作示例)

// Works
var compId = new SqlParameter("@CompanyID", companyId);
compId.Value = (object)companyId ?? DBNull.Value;

var deptId = new SqlParameter("@DepartmentID", departmentId);
deptId.Value = (object)departmentId ?? DBNull.Value;

var locId = new SqlParameter("@LocationID", locationId);
locId.Value = (object)locationId ?? DBNull.Value;

var subId = new SqlParameter("@SubLocationID", sublocationId);
subId.Value = (object)sublocationId ?? DBNull.Value;

var result = context.BudgetWorkflowStatusByDepartment
.FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment_Filtered @CompanyID, @DepartmentID, @LocationID, @SubLocationID", compId, deptId, locId, subId).ToList();


The following works by including the IsNullable=true property of the SqlParameter object.以下通过包含SqlParameter object 的IsNullable=true属性来工作。

// Works
var compId = new SqlParameter("@CompanyID", companyId) { IsNullable = true };
compId.Value = (object)companyId ?? DBNull.Value;

var deptId = new SqlParameter("@DepartmentID", departmentId) { IsNullable = true };
deptId.Value = (object)departmentId ?? DBNull.Value;

var locId = new SqlParameter("@LocationID", locationId) { IsNullable = true };
locId.Value = (object)locationId ?? DBNull.Value;

var subId = new SqlParameter("@SubLocationID", sublocationId) { IsNullable = true };
subId.Value = (object)sublocationId ?? DBNull.Value;

var result = context.BudgetWorkflowStatusByDepartment
        .FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment_Filtered @CompanyID = @CompanyID, @DepartmentID = @DepartmentID, @LocationID = @LocationID, @SubLocationID = @SubLocationID", compId, deptId, locId, subId)
        .ToList();

The following is similar to above but a slightly short-hand syntax method.下面和上面的类似,只是一个稍微简写的语法方法。

// Works
var compId = new SqlParameter("@CompanyID", companyId) { IsNullable = true };
compId.Value = (object)companyId ?? DBNull.Value;

var deptId = new SqlParameter("@DepartmentID", departmentId) { IsNullable = true };
deptId.Value = (object)departmentId ?? DBNull.Value;

var locId = new SqlParameter("@LocationID", locationId) { IsNullable = true };
locId.Value = (object)locationId ?? DBNull.Value;

var subId = new SqlParameter("@SubLocationID", sublocationId) { IsNullable = true };
subId.Value = (object)sublocationId ?? DBNull.Value;

// Works
var result = context.BudgetWorkflowStatusByDepartment
        .FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment_Filtered @CompanyID, @DepartmentID, @LocationID, @SubLocationID", compId, deptId, locId, subId)
        .ToList();

FromSqlInterpolated FromSqlInterpolated

The following works because it uses the .FromSqlInterpolated() method and then the placeholder syntax will work.以下是有效的,因为它使用.FromSqlInterpolated()方法,然后占位符语法将起作用。

// Works
var compId = new SqlParameter("@CompanyID", companyId);
compId.Value = (object)companyId ?? DBNull.Value;

var deptId = new SqlParameter("@DepartmentID", departmentId);
deptId.Value = (object)departmentId ?? DBNull.Value;

var locId = new SqlParameter("@LocationID", locationId);
locId.Value = (object)locationId ?? DBNull.Value;

var subId = new SqlParameter("@SubLocationID", sublocationId);
subId.Value = (object)sublocationId ?? DBNull.Value;

var result = context.BudgetWorkflowStatusByDepartment
    .FromSqlInterpolated($"EXEC mis.BudgetWorkflowStatusByDepartment_Filtered @CompanyID={compId}, @DepartmentID={deptId}, @LocationID={locId}, @SubLocationID={subId}").ToList();

The following is similar to above, but it uses a slightly short-hand syntax.以下与上面类似,但它使用了一种略显简写的语法。

// Works
var compId = new SqlParameter("@CompanyID", companyId);
compId.Value = (object)companyId ?? DBNull.Value;

var deptId = new SqlParameter("@DepartmentID", departmentId);
deptId.Value = (object)departmentId ?? DBNull.Value;

var locId = new SqlParameter("@LocationID", locationId);
locId.Value = (object)locationId ?? DBNull.Value;

var subId = new SqlParameter("@SubLocationID", sublocationId);
subId.Value = (object)sublocationId ?? DBNull.Value;

var result = context.BudgetWorkflowStatusByDepartment
    .FromSqlInterpolated($"EXEC mis.BudgetWorkflowStatusByDepartment_Filtered {compId}, {deptId}, {locId}, {subId}").ToList();

The following is the ultimate in short-hand syntax.以下是速记语法的终极。 With the interpolated method, you can directly use the incoming variables and not have to create the SqlParameter objects.使用插值方法,您可以直接使用传入的变量,而不必创建SqlParameter对象。

// Works
var result = context.BudgetWorkflowStatusByDepartment
    .FromSqlInterpolated($"EXEC mis.BudgetWorkflowStatusByDepartment_Filtered {companyId}, {departmentId}, {locationId}, {sublocationId}").ToList();

Non Working Examples非工作示例

The following code returns an empty array, which is not correct.以下代码返回一个空数组,这是不正确的。 This parameterization of the code can only be used this way with the "interpolated" Sql method.代码的这种参数化只能以这种方式与“插值”Sql 方法一起使用。


// Returns empty array
var result = context.BudgetWorkflowStatusByDepartment
.FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment_Filtered {0}, {1}, {2}, {3}", companyId, departmentId, locationId, companyId).ToList();

Result - an empty array, which is incorrect:结果 - 一个空数组,这是不正确的:

[]

The following method is incorrect in the syntax of the FromSqlRaw method.以下方法在FromSqlRaw方法的语法中不正确。 The parameters in the string want to use interpolation, but this is the wrong method for that syntax.字符串中的参数想要使用插值,但这是该语法的错误方法。

// Error Must declare the scalar variable "@CompanyID".
var compId = new SqlParameter("@CompanyID", companyId);
compId.Value = (object)companyId ?? DBNull.Value;

var deptId = new SqlParameter("@DepartmentID", departmentId);
deptId.Value = (object)departmentId ?? DBNull.Value;

var locId = new SqlParameter("@LocationID", locationId);
locId.Value = (object)locationId ?? DBNull.Value;

var subId = new SqlParameter("@SubLocationID", sublocationId);
subId.Value = (object)sublocationId ?? DBNull.Value;

var result = context.BudgetWorkflowStatusByDepartment
.FromSqlRaw($"EXEC mis.BudgetWorkflowStatusByDepartment_Filtered @CompanyID={compId}, @DepartmentID={deptId}, @LocationID={locId}, @SubLocationID={subId}").ToList();

Error:错误:

Must declare the scalar variable "@CompanyID".

The following is also incorrect.以下也不正确。 The parameters in the string cannot be populated in this manner.字符串中的参数不能以这种方式填充。

// Error Must declare the scalar variable "@CompanyID".
var compId = new SqlParameter("@CompanyID", companyId);
compId.Value = (object)companyId ?? DBNull.Value;

var deptId = new SqlParameter("@DepartmentID", departmentId);
deptId.Value = (object)departmentId ?? DBNull.Value;

var locId = new SqlParameter("@LocationID", locationId);
locId.Value = (object)locationId ?? DBNull.Value;

var subId = new SqlParameter("@SubLocationID", sublocationId);
subId.Value = (object)sublocationId ?? DBNull.Value;

var result = context.BudgetWorkflowStatusByDepartment
.FromSqlRaw($"EXEC mis.BudgetWorkflowStatusByDepartment_Filtered {compId}, {deptId}, {locId}, {subId}").ToList();

Error:错误:

Must declare the scalar variable "@CompanyID".

And lastly, this will also not work because the ??最后,这也不起作用,因为?? operator is setting null values to null instead of DbNull.Value .运算符将 null 值设置为null而不是DbNull.Value


var compId = new SqlParameter("@CompanyID", companyId);
compId.Value = (object)companyId ?? null;

var deptId = new SqlParameter("@DepartmentID", departmentId);
deptId.Value = (object)departmentId ?? null;

var locId = new SqlParameter("@LocationID", locationId);
locId.Value = (object)locationId ?? null;

var subId = new SqlParameter("@SubLocationID", sublocationId);
subId.Value = (object)sublocationId ?? null;

var result = context.BudgetWorkflowStatusByDepartment
.FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment_Filtered @CompanyID, @DepartmentID, @LocationID, @SubLocationID", compId, deptId, locId, subId).ToList();

Error:错误:

The parameterized query '(@CompanyID nvarchar(4000),@DepartmentID nvarchar(4000),@Locatio' expects the parameter '@CompanyID', which was not supplied.

I just re-created a similar setup (same versions of everything you have), and the following one-liner works for me:我刚刚重新创建了一个类似的设置(你拥有的所有东西的相同版本),下面的一行对我有用:

public List<BudgetWorkflowStatusByDepartment> GetBudgetWorkflowStatusByDepartment(int? companyId, int? departmentId, int? locationId, int? sublocationId)
     => context.BudgetWorkflowStatusByDepartment
               .FromSqlInterpolated($"EXEC mis.BudgetWorkflowStatusByDepartment {companyId}, {departmentId}, {locationId}, {subLocationId}").ToList();

Note, since I'm using the .FromSqlInterpolated() call, there is no need to create SqlParameter s - the int?请注意,由于我正在使用.FromSqlInterpolated()调用,因此无需创建SqlParameter s - int? can be passed in directly, as this is a safe call, as per docs , and the null values are properly handled.可以直接传入,因为根据文档,这是一个安全的调用,并且null值已得到妥善处理。

I see that you tried this call as well, but it looks like you're passing SqlParameter s to it, not the data itself - try my version?我看到您也尝试过此调用,但看起来您正在将SqlParameter传递给它,而不是数据本身 - 试试我的版本?

Upon further testing, I got your version to work, with slight modifications, and by setting SqlParameter.IsNullable property to true :经过进一步测试,我让你的版本可以工作,稍作修改,并将SqlParameter.IsNullable属性设置为true

public List<BudgetWorkflowStatusByDepartment> GetBudgetWorkflowStatusByDepartment(int? companyId, int? departmentId, int? locationId, int? sublocationId)
{
    var compId = new SqlParameter("@CompanyID", companyId) { IsNullable = true };
    compId.Value = (object)companyId ?? DBNull.Value;

    var deptId = new SqlParameter("@DepartmentID", departmentId) { IsNullable = true };
    deptId.Value = (object)departmentId ?? DBNull.Value;

    var locId = new SqlParameter("@LocationID", locationId) { IsNullable = true };
    locId.Value = (object)locationId ?? DBNull.Value;

    var subId = new SqlParameter("@SubLocationID", sublocationId) { IsNullable = true };
    subId.Value = (object)sublocationId ?? DBNull.Value;


    var result = context.BudgetWorkflowStatusByDepartment
                        .FromSqlRaw("EXEC mis.BudgetWorkflowStatusByDepartment @CompanyID = @CompanyID, @DepartmentID = @DepartmentID, @LocationID = @LocationID, @SubLocationID = @SubLocationID", compId, deptId, locId, subId)
                        .ToList();

    return result;
}

Take your pick - I personally like the one-liner:)随你挑——我个人喜欢单线:)

The only way I have found to get named parameters (and thus optional parameters) working with do.netcore is to use the following method:我发现使用 do.netcore 获取命名参数(以及可选参数)的唯一方法是使用以下方法:

_context.Database.ExecuteSqlRaw("EXEC mysproc @param1={0}, @param2={1}"
        ,param1
        ,param2);

... where param1 and param2 are of type Microsoft.Data.SqlClient.SqlParameter (this is important, as I have found the hard way that type System.Data.SqlClient.SqlParamater will not generate compile errors but results in confusing exceptions at run time). ...其中 param1 和 param2 的类型为 Microsoft.Data.SqlClient.SqlParameter(这很重要,因为我发现 System.Data.SqlClient.SqlParamater 类型不会生成编译错误但会在运行时导致令人困惑的异常时间)。

While this works consistently, even with output parameters (with the addition of "out" after the parameter placeholder), it is a very inflexible way of creating a statement when your code might provide different outcomes with different sets of parameters.虽然这始终有效,即使使用 output 个参数(在参数占位符后添加“out”),当您的代码可能使用不同的参数集提供不同的结果时,这是一种非常不灵活的创建语句的方式。 One of my use cases has 23 parameters that I might supply, only three of them are required.我的一个用例有 23 个我可能会提供的参数,其中只有三个是必需的。 Hardcoding the ExecuteSqlRaw statement is not an option.硬编码 ExecuteSqlRaw 语句不是一种选择。

I have since come up with a useful method which I use from my Controllers, Pages, etc. where the DbContext _context is injected:从那以后,我想出了一个有用的方法,我在我的控制器、页面等中使用了 DbContext _context 注入的地方:

private void ExecuteStoredProcedure(
        string StoredProcedureName
        ,List<SqlParameter> parameters)
{           
    StringBuilder stringBuilder = null;
    int paramNum = 0;

    // Build the T-SQL statement to enable named paramaters
    foreach (SqlParameter param in parameters)
    {
        string direction = param.Direction == ParameterDirection.Output ? " out" : "";
        if (stringBuilder == null)
        {                    
            stringBuilder = new StringBuilder(string.Format(CultureInfo.CurrentCulture
                    , "EXEC {0} {1}={{{2}}}{3}"
                    , StoredProcedureName
                    , param.ParameterName
                    , paramNum++
                    , direction));
        }
        else
        {
            stringBuilder.Append(string.Format(CultureInfo.CurrentCulture
                , ",{0}={{{1}}}{2}"
                , param.ParameterName
                , paramNum++
                , direction));
        }
    }

    try
    {
        _context.Database.ExecuteSqlRaw(
            stringBuilder.ToString()
            ,parameters.ToArray());
    }
    catch (Exception ex)
    {
         throw;
    }
}

The method take a List of SqlParameters in any order.该方法采用任意顺序的 SqlParameters 列表。 Note that the list is converted to a SqlParamater array before being passed to ExecuteSqlRaw.请注意,列表在传递给 ExecuteSqlRaw 之前会转换为 SqlParamater 数组。 It is not technically a "param array" but I have yet to see it fail.它在技术上不是一个“参数数组”,但我还没有看到它失败。 You may want to pass your DBContext or DatabaseFacade to the method as well.您可能还想将 DBContext 或 DatabaseFacade 传递给该方法。

I typically construct the list of parameters in the following fashion.我通常按以下方式构建参数列表。 The ParameterName property must match the name of the stored procedure parameter that it will feed. ParameterName 属性必须与它将提供的存储过程参数的名称相匹配。 I've excluded any logic for inclusion/exclusion of parameters.我已经排除了包含/排除参数的任何逻辑。

List<SqlParameter> parameters = new List<SqlParameter>();

parameters.Add(new SqlParameter()
    {
        ParameterName = "@MyOutputParam",
        DbType = System.Data.DbType.Int64,
        Direction = System.Data.ParameterDirection.Output
    });

parameters.Add(new SqlParameter()
    {
        ParameterName = "@RequiredButNullableUnicodeString",
        DbType = System.Data.DbType.String,
        Size = 50,
        Value = DBNull.Value
    });

parameters.Add(new SqlParameter()
   {
        ParameterName = "@SomeVarchar",
        DbType = System.Data.DbType.AnsiString,
        Size = 100,
        Value = "some value"
    });

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

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