ChatGPT解决这个技术问题 Extra ChatGPT

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

我对 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 实施起来非常令人沮丧。

u
user692942

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

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

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

Stored Procedures with Multiple Result Sets


谢谢@Alborz。您能否为我提供一些有关 Entity Framework 6 Code First 中存储过程的各种实现的链接。我在网上到处搜索,但没有找到任何可以直接为 IN 和 OUT 参数调用存储过程的文章。感谢您宝贵的时间。
这篇文章可能会有所帮助blogs.msdn.com/b/diego/archive/2012/01/10/…
这似乎不适用于参数。似乎需要明确列出参数作为查询的一部分。
是的,您确实需要将参数指定为查询的一部分 - "storedProcedureName @param1, @param2"params 的类型也是 System.Data.SqlClient.SqlParameter[]
this.Database.SqlQuery<YourEntityType>("storedProcedureName @param1", new System.Data.SqlClient.SqlParameter("@param1", YourParam));
F
Filipe Leite

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

    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


对于一次性情况,这会很好用。我发现 SProc 定义应该与继承自 DBContext 的类紧密耦合,而不是在产品的“麦田”中。
U
Uwe Keim

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

希望对你有所帮助。


您需要在存储过程的 nchar 参数上指定一个长度 - 否则它们只有一个字符长,正如您所发现的。
@Mahdighafoorian 这是一个非常有用的答案,非常感谢! :)
这种语法不需要修改 SProc 参数的顺序,也就是 Ordinal Positioning。
我一直在寻找的 ans
这是书上的正确答案。
m
marc_s

使用您的示例,这里有两种方法可以完成此操作:

方法 #1:使用存储过程映射

请注意,此代码将在有或没有映射的情况下工作。如果关闭实体上的映射,EF 将生成插入 + 选择语句。

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

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


小心,第二个例子是 dbContext 没有跟踪更改
EDIT.Use System.Data.Entity.DbSet.SqlQuery(String, Object[]) 代替。
@edtruant dbContext 似乎确实可以跟踪更改。为了测试,我查看了插入语句前后的 db..Count() 。在这两种方法中,计数都增加了 1。为了完整起见,我在示例中添加了替代方法。
在第一个示例中,我没有看到对存储过程的任何引用。
@xr280xr insert_department 在 OP 问题的 modelBuilder 表达式中被引用。这是以这种方式映射事物的优势,因为它的有效功能与让 EF 生成插入/更新/删除语句的方式相同
q
qujck

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

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

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


谢谢qujck。但我想使用存储过程。为了方便理解,我只给出了一个示例代码。
@Jaan - 上面的代码将使用存储过程。你的意思是要直接调用存储过程?
是的。你能告诉我哪种方式更好。直接调用存储过程还是上面给出的代码?
@Jaan 使用我展示的代码 - ORM 旨在隐藏底层实现 - 使用上面的代码可确保无论是否存在存储过程,其余代码都无关紧要。您甚至可以将模型映射更改为另一个存储过程或不是存储过程,而无需更改任何其他内容。
@Chazt3n 该问题显示了从行 .MapToStoredProcedures(s => 配置的存储过程。对 Add 的调用应解析为 .Insert(i => i.HasName("insert_department")
E
Eric J.

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

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

Read more

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

Read more


实际上,从 4.0 开始,您可以在没有模型的情况下执行 SProcs。您需要执行原始 SQL 语句而不是对象属性。即使使用 6.1.x,您也必须使用 SqlQuery 或 ExecuteSqlCommand 来获得类似的效果。
m
marc_s
object[] xparams = {
            new SqlParameter("@ParameterWithNumvalue", DBNull.Value),
            new SqlParameter("@In_Parameter", "Value"),
            new SqlParameter("@Out_Parameter", SqlDbType.Int) {Direction = ParameterDirection.Output}};

YourDbContext.Database.ExecuteSqlCommand("exec StoredProcedure_Name @ParameterWithNumvalue, @In_Parameter, @Out_Parameter", xparams);
var ReturnValue = ((SqlParameter)params[2]).Value;  

params 是一个标识符,使用不同的名称。
此处的 SaveChanges() 不是必需的。在 ExecuteSqlCommand() 调用中提交更改。
Ω
ΩmegaMan

通过在传递参数时从存储过程中拉回数据,这对我有用。

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

_db 是 dbContext


G
Gabriel Andrés Brancolini

查看此链接,该链接显示 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
                          );

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

希望这个例子有帮助!


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

r
reza.cse08

它首先对我有用。它返回一个具有视图模型匹配属性的列表(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 ,我可以在哪里找到这个 Context.TableName.SqlQuery(ProcName)。这给了我问题
@Marshall,也许您正在使用数据库优先设计。请检查此链接stackoverflow.com/questions/11792018/…
m
marc_s

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

我更喜欢使用 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);
    

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


a
awais

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

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

D
Dib

Mindless passenger 有一个项目,该项目允许使用实体框架从存储过程中返回多个结果集。下面是他的一个例子......

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

m
marc_s

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

以下所有内容都为我工作。这里的 Account 类是 C# 中的实体,具有与数据库中完全相同的表名和列名。

应用配置类如下

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

t
trueboroda

如果要将表参数传递给存储过程,则必须为表参数设置 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();

I
IngoB

这是 EF(DB first)在 DbContext 类中生成的内容:

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

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

K
Kalamarico

当 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.

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

S
SHUBHASIS MAHATA

无需做任何事情...当您为代码优先方法创建 dbcontext 时,在 fluent API 区域下方初始化命名空间,创建 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; }
}

v
vezunchik

首先使用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();

H
Hari Lakkakula

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