如何在Entity Framework 6(代码优先)中调用存储过程?


259

我是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

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
}

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


我也很想知道这一点。理想情况下,我将完全跳过EF并通过除存储过程之外的所有程序运行一切。我是SQL方面的专家,但发现EF实在令人沮丧。
David Britz

Answers:


247

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

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

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

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


2
谢谢@Alborz。您能否提供给我一些有关Entity Framework 6 Code First中存储过程的各种实现的链接。我在网络上到处搜索,但是没有找到任何文章可以直接调用IN和OUT参数的存储过程。感谢您的宝贵时间。
2014年


8
这似乎不适用于参数。似乎需要显式列出参数作为查询的一部分。
标记

6
是的,您确实需要在查询-中指定参数"storedProcedureName @param1, @param2"。的类型params也是System.Data.SqlClient.SqlParameter[]
Oppa Gingham Style

6
this.Database.SqlQuery<YourEntityType>("storedProcedureName @param1", new System.Data.SqlClient.SqlParameter("@param1", YourParam));
Ppp

152

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

    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根据需要使用多个参数进行调用。


2
对于一种情况,这会很好。我发现SProc定义应该与从DBContext继承的类紧密结合,而不是放在产品的“麦田”中。
GoldBishop

50

我解决了 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);
}

这样您就可以在代码中使用如下方法:

[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;
    }
}

这是我的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

希望对你有帮助


2
您需要在存储过程的nchar参数上指定长度-否则,它们只有一个字符长,如您所见。
戴夫W

@Mahdighafoorian这是一个非常有用的答案,非常感谢!:)
Komengem'3

此语法不需要修改SProc参数的顺序,也就是顺序位置。
GoldBishop 2015年

21

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

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;
     }
}

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;
     }
}

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


3
请注意,这是第二个示例,该更改未由dbContext跟踪
edtruant 2016年

编辑。改用System.Data.Entity.DbSet <TEntity> .SqlQuery(String,Object [])。
edtruant '16

@edtruant dbContext确实可以跟踪更改。为了进行测试,我在插入语句之前和之后查看了db。<DbSet> .Count()。在两种方法中,计数都增加一。为了完整起见,我在示例中添加了替代方法。
Brian Vander Plaats '16

1
在第一个示例中,我看不到对存储过程的任何引用。
xr280xr

2
OP的问题中的modelBuilder表达式中引用了@ xr280xr insert_department。这是通过这种方式映射事物的优势,因为它的有效功能与让EF生成插入/更新/删除语句的方式相同
Brian Vander Plaats

15

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

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

如果您真正想做的只是直接调用存储过程,请使用 SqlQuery


2
谢谢qujck。但是我想使用存储过程。我只提供了一个示例代码,以方便理解。
2014年

4
@Jaan-上面的代码使用存储过程。您是说要直接调用存储过程吗?
qujck 2014年

是。你能告诉我哪种方法更好。直接调用存储过程或上面给出的代码?
2014年

6
@Jaan使用我显示的代码-ORM旨在隐藏基础实现-使用上面的代码可确保是否有存储过程与其余代码无关。您甚至可以将模型映射更改为另一个存储过程,也可以不将其更改为存储过程,而无需进行其他任何更改。
qujck 2014年

4
@ Chazt3n问题显示了从该行配置的存储过程.MapToStoredProcedures(s => 。到的呼叫Add应解决.Insert(i => i.HasName("insert_department")
-qujck

12

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

在发布Entity Framework 6.1之前,只能在执行数据库优先时才能在EF中使用存储功能(即表值函数和存储过程)。有一些变通办法可以在Code First应用程序中调用存储功能,但是您仍然不能在Linq查询中使用TVF,这是最大的限制之一。在EF 6.1中,映射API公开了(连同一些其他调整),使得可以在Code First应用程序中使用存储功能。

阅读更多

在过去的两周里,我竭尽全力,现在是–该约定的beta版本,该版本允许在使用Code First方法和Entity Framework 6.1.1(或更高版本)。我对此版本中包含的修复程序和新功能非常满意。

阅读更多


实际上从4.0开始,您可以在没有模型的情况下执行SProcs。您需要执行Raw SQL语句而不是对象属性。即使使用6.1.x,也必须使用SqlQuery <T>或ExecuteSqlCommand来获得类似的效果。
GoldBishop

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;  

1
params是使用不同名称的标识符。
yogihosting

2
此处的SaveChanges()是不必要的。更改在ExecuteSqlCommand()调用中提交。
Xavier Poinas,2016年

10

这对我来说有效,方法是在传入参数的同时从存储过程中提取数据。

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

_db 是dbContext


9

看一下该链接,该链接显示EF 6与存储过程的映射如何进行插入,更新和删除:http : //msdn.microsoft.com/zh-cn/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
                          );

注意,我们对参数使用基于索引的命名。这是因为Entity Framework会将这些参数包装为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();
}

4

它首先对我有用。它返回一个具有视图模型匹配属性的列表(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();

更新了上下文

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

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

var Context = new  ApplicationDbContext();

嗨,我找不到Context.Database.SqlQuery <Model>,因为可以在其中找到Context.TableName.SqlQuery(ProcName)。这给了我一些问题
马歇尔(Marshall)

@Marshall,也许您正在使用数据库优先设计。请检查此链接stackoverflow.com/questions/11792018/…–
reza.cse08

1

没头脑的乘客 有一个项目,该项目允许使用实体框架从存储的proc返回多个结果集。以下是他的例子之一。

using (testentities te = new testentities())
{
    //-------------------------------------------------------------
    // Simple stored proc
    //-------------------------------------------------------------
    var parms1 = new testone() { inparm = "abcd" };
    var results1 = te.CallStoredProc<testone>(te.testoneproc, parms1);
    var r1 = results1.ToList<TestOneResultSet>();
}

1

您可以将参数传递到sp_GetById并在ToList()或中获取结果FirstOrDefault();

var param  = new SqlParameter("@id", 106);
var result = dbContext
               .Database
               .SqlQuery<Category>("dbo.sp_GetById @id", param)
               .FirstOrDefault();

0

如果要将表参数传递给存储过程,则必须为表参数设置TypeName属性。

SqlParameter codesParam = new SqlParameter(CODES_PARAM, SqlDbType.Structured);
            SqlParameter factoriesParam = new SqlParameter(FACTORIES_PARAM, SqlDbType.Structured);

            codesParam.Value = tbCodes;
            codesParam.TypeName = "[dbo].[MES_CodesType]";
            factoriesParam.Value = tbfactories;
            factoriesParam.TypeName = "[dbo].[MES_FactoriesType]";


            var list = _context.Database.SqlQuery<MESGoodsRemain>($"{SP_NAME} {CODES_PARAM}, {FACTORIES_PARAM}"
                , new SqlParameter[] {
                   codesParam,
                   factoriesParam
                }
                ).ToList();

0

这是EF(首先是数据库)在DbContext类中生成的:

public ObjectResult<int> Insert_Department(string department)
{
    var departmentParameter = new ObjectParameter("department", department);

    return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<int>("insert_department", departmentParameter);
}

0

当您在表选择选项中选择存储过程时,当EDMX创建时,只需使用过程名称调用存储过程即可。

var num1 = 1; 
var num2 = 2; 

var result = context.proc_name(num1,num2).tolist();// list or single you get here.. using same thing you can call insert,update or delete procedured.

0

我发现在“代码优先”方法中调用存储过程并不方便。我更喜欢使用Dapper,而不是

以下代码是用编写的Entity Framework

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);

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


0
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();
        }

0

无需执行任何操作...在为代码创建dbcontext时,首先使用方法在流利的API区域下创建名称空间,然后在sp的make列表中使用sp并在其他需要的地方使用它。

public partial class JobScheduleSmsEntities : DbContext
{
    public JobScheduleSmsEntities()
        : base("name=JobScheduleSmsEntities")
    {
        Database.SetInitializer<JobScheduleSmsEntities>(new CreateDatabaseIfNotExists<JobScheduleSmsEntities>());
    }

    public virtual DbSet<Customer> Customers { get; set; }
    public virtual DbSet<ReachargeDetail> ReachargeDetails { get; set; }
    public virtual DbSet<RoleMaster> RoleMasters { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //modelBuilder.Types().Configure(t => t.MapToStoredProcedures());

        //modelBuilder.Entity<RoleMaster>()
        //     .HasMany(e => e.Customers)
        //     .WithRequired(e => e.RoleMaster)
        //     .HasForeignKey(e => e.RoleID)
        //     .WillCascadeOnDelete(false);
    }
    public virtual List<Sp_CustomerDetails02> Sp_CustomerDetails()
    {
        //return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Sp_CustomerDetails02>("Sp_CustomerDetails");
        //  this.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails");
        using (JobScheduleSmsEntities db = new JobScheduleSmsEntities())
        {
           return db.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails").ToList();

        }

    }

}

}

public partial class Sp_CustomerDetails02
{
    public long? ID { get; set; }
    public string Name { get; set; }
    public string CustomerID { get; set; }
    public long? CustID { get; set; }
    public long? Customer_ID { get; set; }
    public decimal? Amount { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public int? CountDay { get; set; }
    public int? EndDateCountDay { get; set; }
    public DateTime? RenewDate { get; set; }
    public bool? IsSMS { get; set; }
    public bool? IsActive { get; set; }
    public string Contact { get; set; }
}

0

首先使用MySql和Entity框架代码的方法:

public class Vw_EMIcount
{
    public int EmiCount { get; set; }
    public string Satus { get; set; }
}

var result = context.Database.SqlQuery<Vw_EMIcount>("call EMIStatus('2018-3-01' ,'2019-05-30')").ToList();

0

在MYsql中创建过程。

delimiter //
create procedure SP_Dasboarddata(fromdate date, todate date)
begin
select count(Id) as count,date,status,sum(amount) as amount from 
details
where (Emidate between fromdate and todate)
group by date ,status;
END;
//

创建包含存储过程返回结果集值的类

[Table("SP_reslutclass")]
public  class SP_reslutclass
{
    [Key]
    public int emicount { get; set; }
    public DateTime Emidate { get; set; }
    public int ? Emistatus { get; set; }
    public int emiamount { get; set; }

}

在Dbcontext中添加类

  public  class ABCDbContext:DbContext
{
    public ABCDbContext(DbContextOptions<ABCDbContext> options)
       : base(options)
    {

    }

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

存储库中的调用实体

   var counts = _Dbcontext.SP_reslutclass.FromSql("call SP_Dasboarddata 
                    ('2019-12-03','2019-12-31')").ToList();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.