web-dev-qa-db-fra.com

Comment appeler procédure stockée dans Entity Framework 6 (Code-First)?

Je suis très nouveau dans Entity Framework 6 et je souhaite implémenter des procédures stockées dans mon projet. J'ai une procédure stockée comme suit:

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
}

Mon problème est: comment puis-je appeler la procédure stockée et y transmettre des paramètres?

248
Jaan

Vous pouvez appeler une procédure stockée dans votre classe DbContext comme suit.

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

Toutefois, si votre procédure stockée renvoie plusieurs ensembles de résultats sous la forme d'un exemple de code, cet article utile est disponible sur MSDN.

Procédures stockées avec plusieurs jeux de résultats

237
Alborz

Tout ce que vous avez à faire est de créer un objet qui porte les mêmes noms de propriété que les résultats renvoyés par la procédure stockée. Pour la procédure stockée suivante:

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

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

créez une classe qui ressemble à:

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

        public string Gender { get; set; }

        public decimal Payout { get; set; }
    }

puis appelez la procédure en procédant comme suit:

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

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

Le résultat contiendra une liste d'objets ResultForCampaign. Vous pouvez appeler SqlQuery en utilisant autant de paramètres que nécessaire.

141
Filipe Leite

Je l'ai résolu avec ExecuteSqlCommand

Mettez votre propre méthode comme la mienne dans DbContext comme vos propres instances:

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

afin que vous puissiez avoir une méthode dans votre code-behind comme ceci:

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

c'est mon 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

l'espoir vous a aidé

47

En utilisant votre exemple, voici deux manières de le faire:

1 - Utiliser le mappage de procédures stockées

Notez que ce code fonctionnera avec ou sans mappage. Si vous désactivez le mappage sur l'entité, EF générera une instruction 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 - Appelez directement la procédure stockée

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

Je recommande d'utiliser la première approche, car vous pouvez travailler directement avec l'objet department sans avoir à créer un groupe d'objets SqlParameter.

19

Vous utilisez MapToStoredProcedures(), qui indique que vous mappez vos entités sur des procédures stockées. Pour ce faire, vous devez ignorer le fait qu'il existe une procédure stockée et utiliser le paramètre context comme d'habitude. Quelque chose comme ça ( écrit dans le navigateur donc pas testé )

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

Si vous essayez uniquement d'appeler directement une procédure stockée, utilisez SqlQuery

15
qujck

Vous pouvez maintenant également utiliser une convention que j'ai créée, qui permet d'appeler des procédures stockées (y compris des procédures stockées renvoyant plusieurs résultats), des fichiers TVF et des fichiers UDF scalaires à partir de EF.

Jusqu'à la publication d'Entity Framework 6.1, les fonctions de magasin (c'est-à-dire les fonctions de table et les procédures stockées) ne pouvaient être utilisées dans EF que lors de l'exécution de Database First. Certaines solutions de contournement ont permis d'invoquer des fonctions de magasin dans les applications Code First, mais vous ne pouviez toujours pas utiliser les TVF dans les requêtes Linq, ce qui était l'une des principales limitations. Dans EF 6.1, l’API de mappage était rendue publique, ce qui (avec quelques ajustements supplémentaires) permettait d’utiliser des fonctions de stockage dans vos applications Code First.

en savoir plus

J'ai poussé assez fort ces deux dernières semaines et voici la version bêta de la convention qui permet d'utiliser des fonctions de magasin (c'est-à-dire des procédures stockées, des fonctions table, etc.) dans des applications utilisant l'approche Code First et Entity Framework 6.1.1 ( Ou plus récent). Je suis plus que satisfait des correctifs et des nouvelles fonctionnalités inclus dans cette version.

en savoir plus .

12
Pawel

Cela fonctionne pour moi en récupérant les données d'une procédure stockée tout en transmettant un paramètre.

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

_db est le dbContext

10
Tom Stickel
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;  
10
Shiraj Momin

Jetez un coup d’œil à ce lien qui montre comment fonctionne le mappage de EF 6 avec les procédures stockées pour effectuer une insertion, une mise à jour et une suppression: http://msdn.Microsoft.com/en-us/data/dn46867 =

Addition

Voici un excellent exemple pour appeler une procédure stockée à partir de Code First:

Disons que vous devez exécuter une procédure stockée avec un seul paramètre et que cette procédure stockée renvoie un ensemble de données correspondant aux états de l'entité. Nous aurons donc ceci:

var countryIso = "AR"; //Argentina

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

Maintenant, disons que nous souhaitons exécuter une autre procédure stockée avec deux paramètres:

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

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

Notez que nous utilisons un nommage basé sur l'index pour les paramètres. En effet, Entity Framework encapsulera ces paramètres sous forme d'objets DbParameter afin d'éviter tout problème d'injection SQL.

J'espère que cet exemple aide!

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

Cela fonctionne pour moi au code d'abord. Il retourne une liste avec la propriété correspondante du modèle de vue (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();

mis à jour pour le contexte

Le contexte est l'instance de la classe qu'Inherit DbContext ressemble à celle ci-dessous.

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

var Context = new  ApplicationDbContext();
4
reza.cse08

Passager sans âme a un projet qui permet de renvoyer plusieurs ensembles de résultats à partir d'un processus stocké à l'aide d'un cadre d'entité. Un de ses exemples ci-dessous ....

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
Dib

si vous voulez passer des paramètres de table dans une procédure stockée, vous devez définir la propriété TypeName pour vos paramètres de table.

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
trueboroda

J'ai trouvé que l'appel de procédures stockées dans l'approche Code First n'est pas pratique. Je préfère utiliser Dapper à la place

Le code suivant a été écrit avec Entity Framework:

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

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

Le code suivant a été écrit avec Dapper:

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

Je crois que le deuxième morceau de code est plus simple à comprendre.

0
Vladislav Furdak

Rien à faire ... lorsque vous créez dbcontext pour le code, commencez par initialiser l'espace de noms sous la zone de l'API fluide, créez la liste de sp et utilisez-la à un autre endroit.

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
SHUBHASIS

Utiliser d'abord le code du framework MySql et 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
Hari Lakkakula
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
user1883961

Voici ce que EF (DB first) génère dans la 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
IngoB

Lorsque EDMX crée cette heure si vous sélectionnez une procédure stockée dans une option de sélection de table, appelez simplement une procédure stockée à l'aide du nom procéduré ...

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
Shafiq Rabbi