Entity Framework 6'da Saklı Yordam nasıl çağırılır (İlk Kod)?


259

Entity Framework 6 için çok yeniyim ve projemde saklı yordamlar uygulamak istiyorum. Aşağıdaki gibi saklı bir yordam var:

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 sınıf:

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
}

Benim sorunum: saklı yordamı nasıl çağırabilir ve içine parametreleri geçirebilirim?


Bunu da bilmek istiyorum. İdeal olarak EF'i tamamen atlarım ve her şeyi saklı yordamlar dışında çalıştırırım. SQL uzmanıyım ama EF'i uygulamak için çok sinir bozucu buldum.
David Britz

Yanıtlar:


247

DbContextSınıfınızda saklı bir yordamı aşağıdaki gibi çağırabilirsiniz .

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

Ancak, saklı yordamınız örnek kodunuz olarak birden çok sonuç kümesi döndürüyorsa, MSDN'de bu yararlı makaleyi görebilirsiniz

Birden Çok Sonuç Kümesiyle Saklı Yordamlar


2
Teşekkürler @Alborz. Lütfen Varlık Çerçevesi 6 Kodunda Saklı Yordamın çeşitli uygulanmasına ilişkin bazı bağlantılar verebilir misiniz? Web üzerinde her yerde aradım ama doğrudan IN ve OUT parametreleri için bir saklı yordam çağırabilirsiniz herhangi bir makale alamadım. Değerli zamanınız için teşekkürler.
Jaan


8
Bu parametrelerle çalışmıyor gibi görünüyor. Parametreleri sorgunun bir parçası olarak açıkça listelemek gerekiyor gibi görünüyor.
Mark

6
Evet, parametreleri - sorgusunun bir parçası olarak belirtmeniz gerekir "storedProcedureName @param1, @param2". Ayrıca tip paramsolduğunu System.Data.SqlClient.SqlParameter[].
Oppa Pötikareli Stil

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

152

Tek yapmanız gereken, saklı yordamın döndürdüğü sonuçlarla aynı özellik adlarına sahip bir nesne oluşturmaktır. Aşağıdaki saklı yordam için:

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

    SELECT AgeGroup, Gender, Payout
    FROM IntegrationResult
    WHERE ClientId = @ClientId
    END

şuna benzer bir sınıf oluşturun:

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

        public string Gender { get; set; }

        public decimal Payout { get; set; }
    }

ve ardından aşağıdakileri yaparak prosedürü çağırın:

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

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

Sonuçta ResultForCampaignnesnelerin bir listesi bulunur . SqlQueryGerektiği kadar parametre kullanarak çağrı yapabilirsiniz .


2
Bir kerelik durumlar için, bu harika olurdu. SProc tanımının ürünün "buğday tarlaları" yerine DBContext'den miras alınan sınıfla sıkı bir şekilde bağlanması gerektiğini düşünüyorum.
GoldBishop

50

İle çözdüm ExecuteSqlCommand

Kendi örnekleriniz olarak DbContext'e benimki gibi kendi yönteminizi koyun:

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

böylece arka plan kodunuzda şöyle bir yöntem olabilir:

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

Bu benim 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

umut sana yardım etti


2
Saklı yordam için nchar parametreleri üzerinde bir uzunluk belirtmeniz gerekir - aksi takdirde bulduğunuz gibi sadece bir karakter uzunluğundadır.
Dave W

@Mahdighafoorian Bu çok faydalı bir cevap, çok teşekkürler! :)
Komengem

Bu sözdizimi, SProc Parametrelerinin düzeninde, yani Sıradan Konumlandırmada herhangi bir değişiklik yapılmasını gerektirmez.
GoldBishop

21

Örneğinizi kullanarak, bunu gerçekleştirmenin iki yolu şunlardır:

1 - Saklı yordam eşlemesini kullanma

Bu kodun eşleme ile veya eşleme olmadan çalışacağını unutmayın. Varlık üzerindeki eşlemeyi kapatırsanız, EF bir insert + select deyimi oluşturur.

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 - Saklı yordamı doğrudan arayın

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

Doğrudan departman nesnesiyle çalışabileceğiniz ve bir grup SqlParameter nesnesi oluşturmak zorunda olmadığınız için ilk yaklaşımı kullanmanızı öneririm.


3
Dikkatli olun, ikinci değişiklik dbContext tarafından izlenmez
edtruant

EDIT Bunun yerine System.Data.Entity.DbSet <TEntity> .SqlQuery (String, Object []) kullanın.
edtruant

@ edtruant Değişikliği izlemek için dbContext görünüyor. Test etmek için, insert deyiminden önce ve sonra db. <DbSet> .Count () 'a baktım. Her iki yöntemde de sayı bir arttı. Bütünlük için örneğe alternatif yöntem ekledim.
Brian Vander Plaats

1
İlk örnekte saklı yordam için herhangi bir başvuru görmüyorum.
xr280xr

2
@ xr280xr, OP_'nin sorusundaki modelBuilder ifadesinde insert_department'a başvurulur. Bu, şeyleri bu şekilde eşlemenin avantajıdır, çünkü EF'in ekleme / güncelleme / silme ifadelerini oluşturmasına izin verdiğiniz gibi etkili bir şekilde işlev görür
Brian Vander Plaats

15

MapToStoredProcedures()Varlıklarınızı saklı yordamlarla eşlediğinizi belirten kullanıyorsunuz , bunu yaparken saklı yordam olduğu gerçeğini bırakmanız ve contextnormal olarak kullanmanız gerekir . Böyle bir şey ( tarayıcıya yazılmış, bu yüzden test edilmedi )

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

Gerçekten yapmaya çalıştığınız tek şey saklı bir prosedürü doğrudan çağırmaksa SqlQuery


2
Teşekkürler qujck. Ama saklı yordamı kullanmak istiyorum. Anlamak için sadece bir örnek kod verdim.
Jaan

4
@Jaan - Yukarıdaki kod olacak saklı prosedürü izleyin. Doğrudan saklı yordamı aramak istediğiniz anlamına mı geliyor?
qujck

Evet. Bana hangi yolun daha iyi olduğunu söyleyebilir misiniz? Doğrudan saklı yordam veya yukarıdaki kodu çağırmak?
Jaan

6
@Jaan, gösterdiğim kodu kullanabilir - ORM, temeldeki uygulamayı gizlemek içindir - yukarıdaki kodu kullanmak, saklı yordam olup olmadığına bakılmaksızın kodunuzun geri kalanı için önemli olmadığını garanti eder. Model eşlemesini başka bir saklı yordamla değiştirebilir veya başka bir şey değiştirmeden saklı yordam olamazsınız.
qujck

4
@ Chazt3n Soru, hattan yapılandırılan saklı yordamları gösterir .MapToStoredProcedures(s => . Add.Insert(i => i.HasName("insert_department")
Çözülmesi

12

Artık oluşturduğum, EF'den yerel olarak saklanan yordamları (birden çok sonuç kümesi döndüren saklı yordamlar dahil), TVF'leri ve skaler UDF'leri çağırmayı sağlayan bir kuralı da kullanabilirsiniz.

Entity Framework 6.1 piyasaya sürülene kadar mağaza işlevleri (yani, Tablo Değerli İşlevler ve Saklı Yordamlar) EF'de yalnızca Önce Veritabanı yapılırken kullanılabilir. Code First uygulamalarında mağaza işlevlerini çağırmayı mümkün kılan bazı geçici çözümler vardı, ancak en büyük sınırlamalardan biri olan Linq sorgularında TVF'leri kullanamıyorsunuz. EF 6.1'de eşleme API'sı herkese açık hale getirildi (bazı ek ayarlarla birlikte) Önce Kod uygulamalarınızda mağaza işlevlerini kullanmayı mümkün kıldı.

Daha fazla oku

Son iki hafta boyunca oldukça zorladım ve işte - Kural İlkesi ve Entity Framework 6.1.1 ( veya daha yeni). Bu sürümdeki düzeltmelerden ve yeni özelliklerden çok memnunum.

Daha fazlasını okuyun .


Aslında 4.0'dan beri, Model olmadan SProcs'u yürütebilirsiniz. Nesne özelliği yerine Raw SQL deyimlerini yürütmeniz gerekiyordu. 6.1.x ile bile, benzer bir etki elde etmek için SqlQuery <T> veya ExecuteSqlCommand kullanmanız gerekir.
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, farklı bir ad kullanan bir tanımlayıcıdır.
yogihosting

2
Buradaki SaveChanges () gerekli değildir. Değişiklikler ExecuteSqlCommand () çağrısında yapılır.
Xavier Poinas

10

Bu benim için, bir parametreden geçerken saklı bir yordamdan veri çekerek işe yarar.

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

_db dbContext


9

Ekleme, Güncelleme ve Silme yapmak için EF 6'nın Saklı Yordamlarla eşlemesinin nasıl çalıştığını gösteren bu bağlantıya bir göz atın: http://msdn.microsoft.com/en-us/data/dn468673

İlave

Kod İlkinden saklı yordamı çağırmak için harika bir örnek:

Saklı Yordam'ı tek bir parametreyle yürütmeniz gerektiğini ve Saklı Yordamın Varlık Durumlarıyla eşleşen bir veri kümesi döndürdüğünü varsayalım, bu nedenle:

var countryIso = "AR"; //Argentina

var statesFromArgentina = context.Countries.SqlQuery(
                                      "dbo.GetStatesFromCountry @p0", countryIso
                                                    );

Şimdi, iki parametreli başka bir saklı yordam yürütmek istediğimizi söyleyelim:

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

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

Parametreler için dizin tabanlı adlandırma kullandığımıza dikkat edin. Bunun nedeni, Entity Framework'ün herhangi bir SQL enjeksiyon sorununu önlemek için bu parametreleri sizin için DbParameter nesneleri olarak sarmasıdır.

Umarım bu örnek yardımcı olur!


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

Önce kodda benim için çalışıyor. Görünüm modelinin eşleşen özelliğine sahip bir liste döndürür (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();

İçerik için güncellendi

Bağlam DbContext'i aşağıdaki gibi devralan sınıfın örneğidir.

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

var Context = new  ApplicationDbContext();

Merhaba, bu Context.TableName.SqlQuery (ProcName) yapabildiğim gibi bu Context.Database.SqlQuery <Model>, bulamıyorum. hangi sorunları veriyor
Marshall

@Marshall, belki de veritabanı ilk tasarımını kullanıyorsunuz. lütfen bu bağlantıyı kontrol edin stackoverflow.com/questions/11792018/…
reza.cse08

1

Dikkatsiz yolcunun , varlık çerçevesi kullanılarak saklanan bir işlemden birden çok sonuç kümesinin döndürülmesine izin veren bir projesi vardır. Aşağıdaki örneklerinden biri ....

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

Parametreleri iletebilir sp_GetByIdve sonuçları ToList()veyaFirstOrDefault();

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

0

tablo parametrelerini saklı yordama geçirmek istiyorsanız, tablo parametreleriniz için TypeName özelliğini ayarlamanız gerekir.

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

DbContext sınıfında EF (önce DB) üretir:

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

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

0

EDMX, tablo seçme seçeneğinde saklı prosedürü seçerseniz bu kez oluşturduğunuzda, sadece prosedürlü ad kullanılarak prosedürü saklayın ...

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

Kod İlkinde Saklı Yordamlar çağrısının uygun olmadığını gördüm. Onun Dapperyerine kullanmayı tercih ederim

Aşağıdaki kod şununla yazılmıştır Entity Framework:

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

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

Aşağıdaki kod şununla yazılmıştır Dapper:

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

İkinci kodun anlaşılması daha kolay olduğuna inanıyorum.


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

Hiçbir şey yapmak zorunda değilsiniz ... kod ilk yaklaşım için dbcontext oluştururken akıcı API alanının altındaki ad alanını başlatmak sp listesi yapmak ve istediğiniz başka bir yerde kullanın.

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

Önce MySql ve Entity çerçeve kodunu kullanma Yaklaşım:

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 içinde yordam oluşturun.

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

Saklı yordam dönüş sonuç kümesi değerlerini içeren sınıf oluşturma

[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'de Sınıf Ekle

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

    }

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

Depodaki çağrı varlığı

   var counts = _Dbcontext.SP_reslutclass.FromSql("call SP_Dasboarddata 
                    ('2019-12-03','2019-12-31')").ToList();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.