Entity Framework 6(Code-First)でストアドプロシージャを呼び出す方法


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の実装は非常にイライラしています。
デビッドブリッツ

回答:


247

DbContext次のように、クラスのストアドプロシージャを呼び出すことができます。

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

ただし、ストアドプロシージャがサンプルコードとして複数の結果セットを返す場合は、MSDNでこの役立つ記事を参照できます。

複数の結果セットを持つストアドプロシージャ


2
@Alborzに感謝します。Entity Framework 6のコードファーストでのストアドプロシージャのさまざまな実装に関するリンクを教えてください。Web上のあらゆる場所を検索しましたが、INおよびOUTパラメータのストアドプロシージャを直接呼び出すことができる記事はありませんでした。貴重なお時間をありがとうございました。
Jaan


8
これはパラメーターでは機能しないようです。クエリの一部としてパラメータを明示的にリストする必要があるようです。
マーク・

6
はい、クエリの一部としてparamsを指定する必要があります- "storedProcedureName @param1, @param2"。のタイプもparamsですSystem.Data.SqlClient.SqlParameter[]
オッパギンガムスタイル

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

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
1回限りの状況では、これはうまく機能します。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パラメータに長さを指定する必要があります-それ以外の場合は、ご存じのように、長さは1文字だけです。
デイブW

@Mahdighafoorianこれは非常に有用な答えです、たくさんありがとう!:)
Komengem 2015年

この構文では、SProcのパラメーターの順序を変更する必要はありません。
GoldBishop 2015年

21

あなたの例を使用して、これを達成する2つの方法があります:

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
2番目の例は、変更がdbContextによって追跡されないことに注意してください
edtruant

代わりに、System.Data.Entity.DbSet <TEntity> .SqlQuery(String、Object [])を使用してください。
edtruant 2016

@edtruant dbContextは変更を追跡しているように見えます。テストするために、挿入ステートメントの前後でdb。<DbSet> .Count()を調べました。どちらの方法でも、カウントは1つ増加しました。完全を期すために、別の方法を例に追加しました。
ブライアンヴァンダープラッツ

1
最初の例では、ストアドプロシージャへの参照がありません。
xr280xr 2017

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 2014年

はい。どっちがいいか教えてください。ストアドプロシージャまたは上記のコードを直接呼び出しますか?
Jaan

6
@Jaanは、私が示したコードを使用します-ORMは基礎となる実装を隠すことを目的としています-上記のコードを使用すると、ストアドプロシージャの有無にかかわらず、コードの残りの部分で問題が発生しなくなります。モデルマッピングを別のストアドプロシージャに変更したり、他に何も変更せずにストアドプロシージャにしないようにすることもできます。
qujck 2014年

4
@ Chazt3n質問は、行から構成されているストアドプロシージャを示しています.MapToStoredProcedures(s => Add解決すべきコール.Insert(i => i.HasName("insert_department")
2015

12

EFからネイティブにストアドプロシージャ(複数の結果セットを返すストアドプロシージャを含む)、TVF、およびスカラーUDFを呼び出すことを可能にする、私が作成した規則を使用することもできます。

Entity Framework 6.1がリリースされるまで、ストアファンクション(つまり、テーブル値関数とストアドプロシージャ)は、データベースファーストを実行する場合にのみEFで使用できました。Code Firstアプリでストア関数を呼び出すことを可能にするいくつかの回避策がありましたが、最大の制限の1つであるLinqクエリでTVFを使用できませんでした。EF 6.1では、マッピングAPIが公開され、(いくつかの追加の調整とともに)Code Firstアプリでストア関数を使用できるようになりました。

続きを読む

過去2週間、私はかなり懸命にプッシュしました。コードファーストアプローチとEntity Framework 6.1.1を使用するアプリケーションでストア関数(つまり、ストアドプロシージャ、テーブル値関数など)を使用できるようにする規約のベータ版です(以降)。このリリースに含まれている修正や新機能に満足しています。

続きを読む


実際には4.0以降、モデルなしでSProcsを実行できました。オブジェクトプロパティの代わりにRaw SQLステートメントを実行する必要がありました。6.1.xでも、同様の効果を得るにはSqlQuery <T>またはExecuteSqlCommandを使用する必要があります。
GoldBishop 2016

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は別の名前を使用する識別子です。
16年

2
ここでのSaveChanges()は必要ありません。変更はExecuteSqlCommand()呼び出しでコミットされます。
ザビエルポイナス

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

ここで、2つのパラメータを使用して別のストアドプロシージャを実行したいとします。

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

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

パラメータにはインデックスベースのネーミングを使用していることに注意してください。これは、SQLインジェクションの問題を回避するために、Entity Frameworkがこれらのパラメーターを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>を見つけることができません。これは私に問題を与えています
マーシャル

@Marshall、おそらくデータベースの最初の設計を使用しています。このリンクを確認してくださいstackoverflow.com/questions/11792018/...
reza.cse08

1

マインドレスパッセンジャーに は、エンティティフレームワークを使用して、ストアドプロシージャから複数の結果セットを返すことができるプロジェクトがあります。以下の彼の例の1つ....

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(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

コードファーストアプローチでのストアドプロシージャの呼び出しは不便であることがわかりました。私は使いたい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);

2番目のコードは理解しやすいと思います。


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とエンティティフレームワークコードを最初に使用する方法:

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.