我对实体框架6非常陌生,我想在我的项目中实现存储过程。我有一个存储过程如下:

ALTER PROCEDURE [dbo].[insert_department]
    @Name [varchar](100)
AS
BEGIN
    INSERT [dbo].[Departments]([Name])
    VALUES (@Name)

    DECLARE @DeptId int

    SELECT @DeptId = [DeptId]
    FROM [dbo].[Departments]
    WHERE @@ROWCOUNT > 0 AND [DeptId] = SCOPE_IDENTITY()

    SELECT t0.[DeptId]
    FROM [dbo].[Departments] AS t0
    WHERE @@ROWCOUNT > 0 AND t0.[DeptId] = @DeptId
END

系类:

public class Department
{
    public int DepartmentId { get; set; }       
    public string Name { get; set; }
}

modelBuilder 
.Entity<Department>() 
.MapToStoredProcedures(s => 
s.Update(u => u.HasName("modify_department") 
               .Parameter(b => b.Department, "department_id") 
               .Parameter(b => b.Name, "department_name")) 
 .Delete(d => d.HasName("delete_department") 
               .Parameter(b => b.DepartmentId, "department_id")) 
 .Insert(i => i.HasName("insert_department") 
               .Parameter(b => b.Name, "department_name")));

protected void btnSave_Click(object sender, EventArgs e)
{
    string department = txtDepartment.text.trim();

    // here I want to call the stored procedure to insert values
}

我的问题是:如何调用存储过程并将参数传递给它?


当前回答

public static string ToSqlParamsString(this IDictionary<string, string> dict)
        {
            string result = string.Empty;
            foreach (var kvp in dict)
            {
                result += $"@{kvp.Key}='{kvp.Value}',";
            }
            return result.Trim(',', ' ');
        }

public static List<T> RunSproc<T>(string sprocName, IDictionary<string, string> parameters)
        {
            string command = $"exec {sprocName} {parameters.ToSqlParamsString()}";
            return Context.Database.SqlQuery<T>(command).ToList();
        }

其他回答

您所要做的就是创建一个对象,该对象具有与存储过程返回的结果相同的属性名。对于以下存储过程:

    CREATE PROCEDURE [dbo].[GetResultsForCampaign]  
    @ClientId int   
    AS
    BEGIN
    SET NOCOUNT ON;

    SELECT AgeGroup, Gender, Payout
    FROM IntegrationResult
    WHERE ClientId = @ClientId
    END

创建一个类,如下所示:

    public class ResultForCampaign
    {
        public string AgeGroup { get; set; }

        public string Gender { get; set; }

        public decimal Payout { get; set; }
    }

然后调用该过程,执行以下操作:

    using(var context = new DatabaseContext())
    {
            var clientIdParameter = new SqlParameter("@ClientId", 4);

            var result = context.Database
                .SqlQuery<ResultForCampaign>("GetResultsForCampaign @ClientId", clientIdParameter)
                .ToList();
    }

结果将包含ResultForCampaign对象的列表。您可以根据需要使用任意多的参数调用SqlQuery。

看一下这个链接,它展示了如何将EF 6与存储过程映射为插入、更新和删除:http://msdn.microsoft.com/en-us/data/dn468673

除了

下面是一个从Code First调用存储过程的好例子:

假设你必须执行一个带有单个参数的存储过程,并且该存储过程返回一组与实体状态匹配的数据,因此我们将有:

var countryIso = "AR"; //Argentina

var statesFromArgentina = context.Countries.SqlQuery(
                                      "dbo.GetStatesFromCountry @p0", countryIso
                                                    );

现在假设我们想要执行另一个带有两个参数的存储过程:

var countryIso = "AR"; //Argentina
var stateIso = "RN"; //Río Negro

var citiesFromRioNegro = context.States.SqlQuery(
                            "dbo.GetCitiesFromState @p0, @p1", countryIso, stateIso
                          );

注意,我们对参数使用基于索引的命名。这是因为实体框架将这些参数包装为DbParameter对象,以避免任何SQL注入问题。

希望这个例子能有所帮助!

.NET Core 5.0没有FromSql,而是有frommsqlraw

下面这些都对我有用。这里的帐户类是c#中的实体,具有与数据库中完全相同的表和列名。

App配置类如下所示

class AppConfiguration
{
    public AppConfiguration()
    {
        var configBuilder = new ConfigurationBuilder();
        var path = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
        configBuilder.AddJsonFile(path, false);
        var root = configBuilder.Build();
        var appSetting = root.GetSection("ConnectionStrings:DefaultConnection");
        sqlConnectionString = appSetting.Value;
    }

    public string sqlConnectionString { get; set; }
}

DbContext类:

public class DatabaseContext : DbContext
{
    public class OptionsBuild
    {
        public OptionsBuild()
        {
            setting = new AppConfiguration();
            opsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
            opsBuilder.UseSqlServer(setting.sqlConnectionString);
            dbOptions = opsBuilder.Options;
        }

        public DbContextOptionsBuilder<DatabaseContext> opsBuilder { get; set; }
        public DbContextOptions<DatabaseContext> dbOptions { get; set; }

        private AppConfiguration setting { get; set; }
    }

    public static OptionsBuild ops = new OptionsBuild();

    public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
    {
        //disable initializer
        //  Database.SetInitializer<DatabaseContext>(null);
    }

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

这段代码应该在你的数据访问层:

List<Account> accounts = new List<Account>();
var context = new DatabaseContext(DatabaseContext.ops.dbOptions);
accounts = await context.Account.ToListAsync();   //direct select from a table

var param = new SqlParameter("@FirstName", "Bill");
accounts = await context.Account.FromSqlRaw<Account>("exec Proc_Account_Select", 
param).ToListAsync();            //procedure call with parameter
        
accounts = context.Account.FromSqlRaw("SELECT * FROM dbo.Account").ToList();  //raw query

您可以在DbContext类中调用存储过程,如下所示。

this.Database.SqlQuery<YourEntityType>("storedProcedureName",params);

但是如果您的存储过程返回多个结果集作为示例代码,那么您可以在MSDN上看到这篇有用的文章

具有多个结果集的存储过程

我发现以代码优先的方式调用存储过程并不方便。

我更喜欢用Dapper代替。

下面的代码是用实体框架编写的:

var clientIdParameter = new SqlParameter("@ClientId", 4);

var result = context.Database
.SqlQuery<ResultForCampaign>("GetResultsForCampaign @ClientId", clientIdParameter)
.ToList();

以下代码是用Dapper编写的:

return Database.Connection.Query<ResultForCampaign>(
            "GetResultsForCampaign ",
            new
            {
                ClientId = 4
            },
            commandType: CommandType.StoredProcedure);
    

我认为第二段代码更容易理解。