如何在实体框架6(代码优先)中调用存储过程?

Jaa*_*aan 253 c# stored-procedures entity-framework

我是Entity Framework 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
Run Code Online (Sandbox Code Playgroud)

Department 类:

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
}
Run Code Online (Sandbox Code Playgroud)

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

Alb*_*orz 242

您可以DbContext按如下方式在类中调用存储过程.

this.Database.SqlQuery<YourEntityType>("storedProcedureName",params);
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这似乎不适用于参数.它似乎需要明确列出参数作为查询的一部分. (7认同)
  • `this.Database.SqlQuery&lt;YourEntityType&gt;("storedProcedureName @param1", new System.Data.SqlClient.SqlParameter("@param1", YourParam));` (6认同)
  • 是的,你需要指定params作为查询的一部分 - ""storedProcedureName @ param1,@ param2"`.`params`的类型也是`System.Data.SqlClient.SqlParameter []`. (5认同)
  • 谢谢@Alborz.能否请您提供一些关于实体框架6 Code First中存储过程的各种实现的链接.我在网上到处搜索,但没有得到任何文章,我可以直接调用IN和OUT参数的存储过程.谢谢你宝贵的时间. (2认同)
  • 本文可能有用http://blogs.msdn.com/b/diego/archive/2012/01/10/how-to-execute-stored-procedures-sqlquery-in-the-dbcontext-api.aspx (2认同)
  • 这个答案是正确的,但没有解决您可以使用存储过程映射来避免使用 Database.SQLQuery() 的问题。查看我的答案或 qujck 的 (2认同)

Fil*_*ite 142

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

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

    SELECT AgeGroup, Gender, Payout
    FROM IntegrationResult
    WHERE ClientId = @ClientId
    END
Run Code Online (Sandbox Code Playgroud)

创建一个类似于的类:

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

        public string Gender { get; set; }

        public decimal Payout { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

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

结果将包含一个ResultForCampaign对象列表.您可以SqlQuery根据需要使用尽可能多的参数调用.

  • 对于一次性情况,这将很有效.我发现SProc定义应该与继承自DBContext的类紧密耦合,而不是在产品的"麦田"中. (2认同)

Mah*_*ian 47

我用它解决了 ExecuteSqlCommand

把你自己的方法像我一样在DbContext中作为你自己的实例:

public void addmessage(<yourEntity> _msg)
{
    var date = new SqlParameter("@date", _msg.MDate);
    var subject = new SqlParameter("@subject", _msg.MSubject);
    var body = new SqlParameter("@body", _msg.MBody);
    var fid = new SqlParameter("@fid", _msg.FID);
    this.Database.ExecuteSqlCommand("exec messageinsert @Date , @Subject , @Body , @Fid", date,subject,body,fid);
}
Run Code Online (Sandbox Code Playgroud)

所以你可以在你的代码隐藏中使用这样的方法:

[WebMethod] //this method is static and i use web method because i call this method from client side
public static void AddMessage(string Date, string Subject, string Body, string Follower, string Department)
{
    try
    {
        using (DBContext reposit = new DBContext())
        {
            msge <yourEntity> Newmsg = new msge();
            Newmsg.MDate = Date;
            Newmsg.MSubject = Subject.Trim();
            Newmsg.MBody = Body.Trim();
            Newmsg.FID= 5;
            reposit.addmessage(Newmsg);
        }
    }
    catch (Exception)
    {
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的SP:

Create PROCEDURE dbo.MessageInsert

    @Date nchar["size"],
    @Subject nchar["size"],
    @Body nchar["size"],
    @Fid int
AS
    insert into Msg (MDate,MSubject,MBody,FID) values (@Date,@Subject,@Body,@Fid)
    RETURN
Run Code Online (Sandbox Code Playgroud)

希望能帮助你

  • 您需要在存储过程的nchar参数上指定一个长度 - 否则它们只是一个字符长,如您所见. (2认同)

Bri*_*ats 19

使用您的示例,有两种方法可以实现此目的:

1 - 使用存储过程映射

请注意,此代码可以使用或不使用映射.如果关闭实体上的映射,EF将生成insert + select语句.

protected void btnSave_Click(object sender, EventArgs e)
{
     using (var db = DepartmentContext() )
     {
        var department = new Department();

        department.Name = txtDepartment.text.trim();

        db.Departments.add(department);
        db.SaveChanges();

        // EF will populate department.DepartmentId
        int departmentID = department.DepartmentId;
     }
}
Run Code Online (Sandbox Code Playgroud)

2 - 直接调用存储过程

protected void btnSave_Click(object sender, EventArgs e)
{
     using (var db = DepartmentContext() )
     {
        var name = new SqlParameter("@name", txtDepartment.text.trim());

        //to get this to work, you will need to change your select inside dbo.insert_department to include name in the resultset
        var department = db.Database.SqlQuery<Department>("dbo.insert_department @name", name).SingleOrDefault();

       //alternately, you can invoke SqlQuery on the DbSet itself:
       //var department = db.Departments.SqlQuery("dbo.insert_department @name", name).SingleOrDefault();

        int departmentID = department.DepartmentId;
     }
}
Run Code Online (Sandbox Code Playgroud)

我建议使用第一种方法,因为您可以直接使用department对象而不必创建一堆SqlParameter对象.

  • 注意,第二个例子是dbContext没有跟踪更改 (3认同)
  • 我在第一个示例中没有看到对存储过程的任何引用。 (3认同)
  • @ xr280xr在OP的问题中的modelBuilder表达式中引用了insert_department.这是以这种方式映射事物的优势,因为它的有效功能与让EF生成插入/更新/删除语句的方式相同 (2认同)

quj*_*jck 15

您正在使用MapToStoredProcedures()它表示您正在将实体映射到存储过程,在执行此操作时,您需要放弃存储过程并使用context正常情况的事实.像这样的东西(写入浏览器所以未经测试)

using(MyContext context = new MyContext())
{
    Department department = new Department()
    {
        Name = txtDepartment.text.trim()
    };
    context.Set<Department>().Add(department);
}
Run Code Online (Sandbox Code Playgroud)

如果你真正想要做的就是直接调用存储过程然后使用 SqlQuery

  • @Jaan使用我已经显示的代码 - ORM旨在隐藏底层实现 - 使用上面的代码确保代码的其余部分无论是否有存储过程都无关紧要.您甚至可以将模型映射更改为另一个存储过程,或者不更改存储过程而不更改其他任何内容. (6认同)
  • @Jaan - 上面的代码*将*使用存储过程.你的意思是你想直接调用存储过程吗? (4认同)
  • @ Chazt3n问题显示存储过程是从`.MapToStoredProcedures(s =>`)行配置的.对`Add`的调用应解析为`.Insert(i => i.HasName("insert_department")` (4认同)
  • 谢谢qujck.但我想使用存储过程.我只提供了一个示例代码,以方便理解. (2认同)

Paw*_*wel 12

您现在还可以使用我创建的约定,该约定允许从EF本地调用存储过程(包括返回多个结果集的存储过程),TVF和标量UDF.

在实体框架6.1发布之前,只有在执行数据库优先时,才能在EF中使用存储功能(即表值函数和存储过程).有一些解决方法可以在Code First应用程序中调用商店功能,但您仍然无法在Linq查询中使用TVF,这是最大的限制之一.在EF 6.1中,映射API被公开(随着一些额外的调整)使得在Code First应用程序中使用商店功能成为可能.

阅读更多

在过去的两周里,我非常努力地推出了这个约定的beta版本,它允许在使用Code First方法和Entity Framework 6.1.1的应用程序中使用存储函数(即存储过程,表值函数等)(或更新).我对此版本中包含的修复程序和新功能非常满意.

阅读更多.


Shi*_*min 10

object[] xparams = {
            new SqlParameter("@ParametterWithNummvalue", DBNull.Value),
            new SqlParameter("@In_Parameter", "Value"),
            new SqlParameter("@Out_Parameter", SqlDbType.Int) {Direction = ParameterDirection.Output}};

        YourDbContext.Database.ExecuteSqlCommand("exec StoreProcedure_Name @ParametterWithNummvalue, @In_Parameter, @Out_Parameter", xparams);
        var ReturnValue = ((SqlParameter)params[2]).Value;  
Run Code Online (Sandbox Code Playgroud)

  • 这里的SaveChanges()不是必需的.更改将在ExecuteSqlCommand()调用时提交. (2认同)

Tom*_*kel 10

这通过在传入参数时从存储过程中拉回数据来为我工作.

var param = new SqlParameter("@datetime", combinedTime);
var result = 
        _db.Database.SqlQuery<QAList>("dbo.GetQAListByDateTime @datetime", param).ToList();
Run Code Online (Sandbox Code Playgroud)

_db 是dbContext


Gab*_*ini 9

看一下这个链接,该链接显示了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
                                                    );
Run Code Online (Sandbox Code Playgroud)

现在让我们说我们要用两个参数执行另一个存储过程:

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

var citiesFromRioNegro = context.States.SqlQuery(
                            "dbo.GetCitiesFromState @p0, @p1", countryIso, stateIso
                          );
Run Code Online (Sandbox Code Playgroud)

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

希望这个例子有帮助!


小智 6

public IList<Models.StandardRecipeDetail> GetRequisitionDetailBySearchCriteria(Guid subGroupItemId, Guid groupItemId)
{
    var query = this.UnitOfWork.Context.Database.SqlQuery<Models.StandardRecipeDetail>("SP_GetRequisitionDetailBySearchCriteria @SubGroupItemId,@GroupItemId",
    new System.Data.SqlClient.SqlParameter("@SubGroupItemId", subGroupItemId),
    new System.Data.SqlClient.SqlParameter("@GroupItemId", groupItemId));
    return query.ToList();
}
Run Code Online (Sandbox Code Playgroud)


rez*_*e08 5

它首先对我有用。它返回一个具有视图模型匹配属性的列表(StudentChapterCompletionViewModel)

var studentIdParameter = new SqlParameter
{
     ParameterName = "studentId",
     Direction = ParameterDirection.Input,
     SqlDbType = SqlDbType.BigInt,
     Value = studentId
 };

 var results = Context.Database.SqlQuery<StudentChapterCompletionViewModel>(
                "exec dbo.sp_StudentComplettion @studentId",
                 studentIdParameter
                ).ToList();
Run Code Online (Sandbox Code Playgroud)

更新了上下文

Context 是继承 DbContext 的类的实例,如下所示。

public class ApplicationDbContext : DbContext
{
    public DbSet<City> City { get; set; }
}

var Context = new  ApplicationDbContext();
Run Code Online (Sandbox Code Playgroud)


Vla*_*dak 5

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

我更喜欢用它Dapper来代替。

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

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

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

以下代码是用 编写的Dapper

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

我相信第二段代码更容易理解。