Come chiamare Stored Procedure in Entity Framework 6 (Code-First)?


259

Sono molto nuovo di Entity Framework 6 e voglio implementare le procedure memorizzate nel mio progetto. Ho una procedura memorizzata come segue:

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 classe:

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
}

Il mio problema è: come posso chiamare la procedura memorizzata e passarci i parametri?


Sono interessato a saperlo anche io. Idealmente, salterei del tutto EF e gestivo TUTTO attraverso nient'altro che stored procedure. Sono un esperto di SQL ma ho trovato EF molto frustrante da implementare.
David Britz,

Risposte:


247

È possibile chiamare una procedura memorizzata nella DbContextclasse come segue.

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

Ma se la procedura memorizzata restituisce più set di risultati come codice di esempio, è possibile visualizzare questo utile articolo su MSDN

Stored procedure con set di risultati multipli


2
Grazie @Alborz. potete per favore fornirmi alcuni link riguardanti l'implementazione della Stored Procedure nel codice Entity Framework 6 First. Ho cercato ovunque sul Web ma non ho ricevuto alcun articolo in cui posso chiamare direttamente una procedura memorizzata per i parametri IN e OUT. Grazie per il tuo prezioso tempo.
Jaan,

2
Questo articolo potrebbe essere
Alborz,

8
Questo non sembra funzionare con i parametri. Sembra che sia necessario elencare esplicitamente i parametri come parte della query.
Segna il

6
Sì, è necessario specificare i parametri come parte della query - "storedProcedureName @param1, @param2". Anche il tipo di paramsè System.Data.SqlClient.SqlParameter[].
Oppa Gingham Style

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

152

Tutto quello che devi fare è creare un oggetto con gli stessi nomi di proprietà dei risultati restituiti dalla procedura memorizzata. Per la seguente procedura memorizzata:

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

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

crea una classe che assomigli a:

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

        public string Gender { get; set; }

        public decimal Payout { get; set; }
    }

e quindi chiamare la procedura procedendo come segue:

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

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

Il risultato conterrà un elenco di ResultForCampaignoggetti. È possibile chiamare SqlQueryutilizzando tutti i parametri necessari.


2
Per le situazioni una tantum, funzionerebbe benissimo. Trovo che la definizione SProc debba essere strettamente accoppiata alla classe che eredita da DBContext, anziché nei "campi di grano" del prodotto.
GoldBishop il

50

L'ho risolto con ExecuteSqlCommand

Inserisci il tuo metodo come il mio in DbContext come istanze personali:

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

così puoi avere un metodo nel code-behind in questo modo:

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

questo è il mio 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

la speranza ti ha aiutato


2
È necessario specificare una lunghezza sui parametri nchar per la procedura memorizzata, altrimenti sono lunghi solo un carattere, come hai trovato.
Dave W,

@Mahdighafoorian Questa è una risposta molto utile, grazie mille! :)
Komengem

Questa sintassi non richiede alcuna modifica all'ordine dei parametri di SProc, in altre parole Posizionamento ordinale.
GoldBishop,

21

Usando il tuo esempio, qui ci sono due modi per farlo:

1 - Utilizzare la mappatura delle procedure memorizzate

Si noti che questo codice funzionerà con o senza mappatura. Se disattivi il mapping sull'entità, EF genererà un'istruzione 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 - Chiamare direttamente la procedura memorizzata

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

Consiglio di usare il primo approccio, poiché puoi lavorare direttamente con l'oggetto department e non devi creare un gruppo di oggetti SqlParameter.


3
Fai attenzione, è il secondo esempio che la modifica non è tracciata da dbContext
edtruant

EDIT.Usare invece System.Data.Entity.DbSet <TEntity> .SqlQuery (String, Object []).
edtruant,

@edtruant Il dbContext sembra tracciare la modifica. Per testare, ho esaminato db. <DbSet> .Count () prima e dopo l'istruzione insert. In entrambi i metodi, il conteggio è aumentato di uno. Per completezza ho aggiunto il metodo alternativo all'esempio.
Brian Vander Plaats,

1
Non vedo alcun riferimento alla procedura memorizzata nel primo esempio.
xr280xr,

2
@ xr280xr il parametro insert_department è indicato nell'espressione modelBuilder nella domanda dell'OP. Questo è il vantaggio di mappare le cose in questo modo perché funziona in modo efficace come se si stesse lasciando che EF generasse le istruzioni insert / update / delete
Brian Vander Plaats,

15

Stai usando ciò MapToStoredProcedures()che indica che stai mappando le tue entità alle procedure memorizzate, quando fai questo devi lasciar andare il fatto che esiste una procedura memorizzata e usare il contextnormale. Qualcosa del genere ( scritto nel browser quindi non testato )

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

Se tutto ciò che stai davvero cercando di fare è chiamare direttamente una procedura memorizzata, allora usa SqlQuery


2
Grazie amico. Ma voglio usare la procedura memorizzata. Ho fornito solo un codice di esempio per una comoda comprensione.
Jaan,

4
@Jaan - Il codice di cui sopra sarà utilizzare la stored procedure. Vuoi dire che vuoi chiamare direttamente la procedura memorizzata?
Qujck,

sì. Potete per favore dirmi in che modo è meglio. Chiamare direttamente la procedura memorizzata o il codice sopra indicato?
Jaan,

6
@Jaan usa il codice che ho mostrato - l'ORM ha lo scopo di nascondere l'implementazione sottostante - l'uso del codice sopra assicura che non abbia importanza per il resto del codice se c'è una procedura memorizzata o meno. È anche possibile modificare la mappatura del modello in un'altra procedura memorizzata o non essere una procedura memorizzata senza modificare nient'altro.
Qujck,

4
@ Chazt3n La domanda mostra le procedure memorizzate in fase di configurazione dalla linea .MapToStoredProcedures(s => . Una chiamata a Adddovrebbe risolversi in.Insert(i => i.HasName("insert_department")
qujck

12

Ora puoi anche utilizzare una convenzione che ho creato che consente di richiamare nativamente le procedure memorizzate (comprese le procedure memorizzate che restituiscono più set di risultati), TVF e UDF scalari da EF.

Fino al rilascio di Entity Framework 6.1, le funzioni del negozio (ovvero le funzioni con valori di tabella e le procedure memorizzate) potevano essere utilizzate in EF solo quando si utilizza Database First. Ci sono state alcune soluzioni alternative che hanno reso possibile richiamare le funzioni dello store nelle app Code First, ma non è stato ancora possibile utilizzare i TVF nelle query Linq, che è stata una delle maggiori limitazioni. In EF 6.1 l'API di mappatura è stata resa pubblica che (insieme ad alcune modifiche aggiuntive) ha reso possibile l'uso delle funzioni di archivio nelle app Code First.

Leggi di più

Ho spinto piuttosto forte nelle ultime due settimane ed eccolo qui: la versione beta della convenzione che consente di utilizzare le funzioni store (ovvero stored procedure, funzioni con valori di tabella ecc.) In applicazioni che utilizzano l'approccio Code First e Entity Framework 6.1.1 ( o più recente). Sono più che soddisfatto delle correzioni e delle nuove funzionalità incluse in questa versione.

Per saperne di più .


In realtà dalla 4.0, è possibile eseguire SProcs senza il modello. È necessario eseguire istruzioni SQL non elaborate anziché la proprietà dell'oggetto. Anche con 6.1.x, è necessario utilizzare SqlQuery <T> o ExecuteSqlCommand per ottenere un effetto simile.
GoldBishop il

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 è un identificatore usa un nome diverso.
Yogihosting

2
SaveChanges () qui non è necessario. Le modifiche vengono confermate alla chiamata ExecuteSqlCommand ().
Xavier Poinas,

10

Questo funziona per me estraendo i dati da una procedura memorizzata mentre si passa un parametro.

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

_db è il dbContext


9

Dai un'occhiata a questo link che mostra come funziona la mappatura di EF 6 con Stored Procedures per effettuare un inserimento, aggiornamento ed eliminazione: http://msdn.microsoft.com/en-us/data/dn468673

aggiunta

Ecco un ottimo esempio per chiamare una stored procedure da Code First:

Diciamo che devi eseguire una Stored Procedure con un singolo parametro e che Stored Procedure restituisce una serie di dati che corrispondono agli Stati entità, quindi avremo questo:

var countryIso = "AR"; //Argentina

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

Ora supponiamo che desideriamo eseguire un'altra procedura memorizzata con due parametri:

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

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

Si noti che stiamo utilizzando la denominazione basata su indice per i parametri. Questo perché Entity Framework impacchetterà questi parametri come oggetti DbParameter per evitare problemi di iniezione SQL.

Spero che questo esempio aiuti!


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

Prima funziona per me al codice. Restituisce un elenco con la proprietà corrispondente del modello di visualizzazione (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();

Aggiornato per il contesto

Il contesto è l'istanza della classe che eredita DbContext come di seguito.

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

var Context = new  ApplicationDbContext();

Salve, non riesco a trovare questo Context.Database.SqlQuery <Model>, dove come posso fare questo Context.TableName.SqlQuery (ProcName). che mi sta dando problemi
Marshall,

@ Marshall, forse stai usando il primo design del database. si prega di consultare questo link stackoverflow.com/questions/11792018/...
reza.cse08

1

Il passeggero senza cervello ha un progetto che consente di restituire più set di risultati da un proc memorizzato utilizzando il framework di entità. Uno dei suoi esempi di seguito ....

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

È possibile passare i parametri sp_GetByIde recuperare i risultati in ToList()oFirstOrDefault();

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

0

se si desidera passare i parametri della tabella alla stored procedure, è necessario impostare la proprietà TypeName per i parametri della tabella.

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

Questo è ciò che EF (DB prima) genera nella classe DbContext:

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

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

0

Quando EDMX crea questa volta se si seleziona l'opzione memorizzata nella procedura selezionata nella tabella, è sufficiente chiamare l'archiviazione della procedura utilizzando il nome della procedura ...

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

Ho scoperto che chiamare l'approccio Stored Procedures in Code First non è conveniente. Preferisco usare Dapperinvece

Il seguente codice è stato scritto con Entity Framework:

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

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

Il seguente codice è stato scritto con Dapper:

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

Credo che il secondo pezzo di codice sia più semplice da capire.


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

Non c'è niente da fare ... quando si crea dbcontext per il primo approccio al codice, inizializzare lo spazio dei nomi sotto l'area API fluente, fare un elenco di sp e usarlo in un altro posto dove si desidera.

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

Utilizzo del codice framework MySql ed Entity primo approccio:

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

Creare una procedura in 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;
//

Creare una classe che contiene valori di set di risultati di restituzione della stored procedure

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

}

Aggiungi classe in Dbcontext

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

    }

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

Chiama entità nel repository

   var counts = _Dbcontext.SP_reslutclass.FromSql("call SP_Dasboarddata 
                    ('2019-12-03','2019-12-31')").ToList();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.