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

답변:


247

DbContext다음과 같이 클래스 에서 저장 프로 시저를 호출 할 수 있습니다 .

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

그러나 저장 프로 시저가 여러 결과 집합을 샘플 코드로 반환하면 MSDN에서이 유용한 기사를 볼 수 있습니다.

여러 결과 집합이있는 저장 프로 시저


2
감사합니다 @Alborz. Entity Framework 6 Code First에서 저장 프로 시저의 다양한 구현과 관련된 링크를 제공하십시오. 웹의 모든 곳을 검색했지만 IN 및 OUT 매개 변수에 대한 저장 프로 시저를 직접 호출 할 수있는 기사를 얻지 못했습니다. 소중한 시간 감사합니다.
Jaan

2
이 기사가 도움이
Alborz

8
매개 변수와 작동하지 않는 것 같습니다. 쿼리의 일부로 매개 변수를 명시 적으로 나열해야하는 것 같습니다.
Mark

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 매개 변수의 길이를 지정해야합니다. 그렇지 않으면 찾은대로 한 문자 길이입니다.
Dave W

@Mahdighafoorian 이것은 매우 유용한 답변입니다. 감사합니다! :)
Komengem

이 구문은 SProc의 매개 변수 순서, 즉 서수 위치 지정을 수정하지 않아도됩니다.
GoldBishop

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

부서 개체를 직접 사용하여 많은 SqlParameter 개체를 만들 필요가 없으므로 첫 번째 방법을 사용하는 것이 좋습니다.


3
주의, 변화가 dbContext에 의해 추적되지 않습니다 두 번째 예이다
edtruant

EDIT. System.Data.Entity.DbSet <TEntity> .SqlQuery (String, Object [])를 대신 사용하십시오.
edtruant

@edtruant dbContext가 변경 사항을 추적하는 것처럼 보입니다. 테스트하기 위해 insert 문 전후에 db. <DbSet> .Count ()를 살펴 보았습니다. 두 방법 모두 개수가 1 증가했습니다. 완전성을 위해 대체 방법을 예제에 추가했습니다.
Brian Vander Plaats

1
첫 번째 예에서는 저장 프로 시저에 대한 참조가 없습니다.
xr280xr

2
@ xr280xr insert_department는 OP의 질문에있는 modelBuilder 표현식에서 참조됩니다. 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. 그러나 저장 프로 시저를 사용하고 싶습니다. 이해하기 쉽도록 샘플 코드 만 제공했습니다.
Jaan

4
@Jaan - 위의 코드는 것이다 저장 프로 시저를 사용합니다. 저장 프로 시저를 직접 호출 하시겠습니까?
qujck

예. 어떤 방법이 더 낫는지 말씀해 주시겠습니까? 저장 프로 시저 또는 위의 코드를 직접 호출 하시겠습니까?
자안

6
@Jaan은 내가 보여준 코드를 사용합니다. ORM은 기본 구현을 숨겨야합니다. 위의 코드를 사용하면 저장 프로 시저가 있는지 여부에 관계없이 나머지 코드에 문제가되지 않습니다. 모델 맵핑을 다른 스토어드 프로 시저로 변경하거나 다른 것을 변경하지 않고 스토어드 프로 시저가 아님을 변경할 수도 있습니다.
qujck

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 앱에서 상점 기능을 사용할 수있었습니다.

더 읽어보기

필자는 지난 2 주 동안 매우 열심히 노력했으며 코드 우선 접근 방식 및 Entity Framework 6.1.1 ( 이상). 이 릴리스에 포함 된 수정 사항 및 새로운 기능에 만족합니다.

더 읽어보십시오 .


실제로 4.0 이후 모델없이 SProcs를 실행할 수 있습니다. 객체 속성 대신 원시 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 2016 년

2
여기서 SaveChanges ()는 필요하지 않습니다. ExecuteSqlCommand () 호출에서 변경 내용이 커밋됩니다.
Xavier Poinas

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/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는 SQL 주입 문제를 피하기 위해 이러한 매개 변수를 DbParameter 개체로 래핑하기 때문입니다.

이 예제가 도움이 되길 바랍니다!


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

Mindless 승객 은 엔티티 프레임 워크를 사용하여 저장된 결과에서 여러 결과 세트를 리턴 할 수있는 프로젝트가 있습니다. 아래의 예 중 하나는 ...

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

이것이 Eb (DB 우선)가 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

Code First 접근 방식에서 Stored Procedures를 호출하는 것이 편리하지 않다는 것을 알았습니다. 나는 사용하는 것을 선호합니다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 목록을 작성하고 원하는 다른 위치에서 사용할 수 있습니다.

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();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.