Як викликати збережену процедуру в 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 Code. Я шукав всюди в Інтернеті, але не отримав жодної статті, де я можу безпосередньо викликати збережену процедуру для параметрів IN та OUT. Дякуємо за ваш цінний час
Яан

2
Ця стаття може бути корисна blogs.msdn.com/b/diego/archive/2012/01/10 / ...
Alborz

8
Схоже, це не працює з параметрами. Здається, потрібно чітко перераховувати параметри як частину запиту.
Марк

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

це мій СП:

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 для вашої збереженої процедури - інакше вони мають лише один символ, як ви знайшли.
Дейв Ш

@Mahdighafoorian Це дуже корисна відповідь, дякую багато! :)
Коменгем

Цей синтаксис не потребує змін до порядку параметрів SProc, інакше кажучи, звичайного розміщення.
GoldBishop

21

Використовуючи свій приклад, ось два способи досягти цього:

1 - Використовуйте відображення збережених процедур

Зауважте, що цей код буде працювати з відображенням або без нього. Якщо вимкнути відображення сутності, EF генерує оператор вставки + 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 Здається, dbContext відстежує зміни. Щоб перевірити, я переглянув db. <DbSet> .Count () до і після оператора вставлення. В обох методах кількість збільшилася на одиницю. Для повноти я додав у приклад альтернативний метод.
Brian Vander Plaats

1
У першому прикладі я не бачу посилання на збережену процедуру.
xr280xr

2
@ xr280xr на вказівку insert_department йдеться у виразі 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
Дякую квітка. Але я хочу використовувати збережену процедуру. Я дав лише зразок коду для зручного розуміння.
Яан

4
@Jaan - У наведеному вище коді буде використана збережена процедура. Ви маєте на увазі, що хочете безпосередньо викликати збережену процедуру?
qujck

так. Скажіть, будь ласка, в який спосіб краще. Викликає безпосередньо збережену процедуру або вказаний вище код?
Яан

6
@Jaan використовуйте код, який я показав - ORM призначений для приховування основної реалізації - використання коду вище гарантує, що для решти коду це не має значення, зберігається процедура чи ні. Можна навіть змінити відображення моделі на іншу збережену процедуру або не бути збереженою процедурою, не змінюючи нічого іншого.
qujck

4
@ Chazt3n Питання показує збережені процедури, налаштовані з рядка .MapToStoredProcedures(s => . Заклик до Addповинен вирішити до.Insert(i => i.HasName("insert_department")
qujck

12

Тепер ви також можете скористатись створеною мною конвенцією, яка дозволяє викликати збережені процедури (включаючи збережені процедури, що повертають кілька наборів результатів), TVF та скалярні UDF, вихідно з EF.

Поки не було випущено Entity Framework 6.1, функції зберігання (тобто функції, оцінені у таблиці та збережені процедури) можна було використовувати в EF лише при першому виконанні бази даних. Існували деякі шляхові вирішення, які дозволили викликати функції магазину в додатках Code First, але ви все ще не можете використовувати TVF в запитах Linq, що було одним з найбільших обмежень. У EF 6.1 картографічний API був оприлюднений, що (поряд з деякими додатковими налаштуваннями) дозволило використовувати функції магазину у своїх програмах Code First.

Детальніше

Останні два тижні я наполягав досить сильно, і ось це - бета-версія конвенції, яка дозволяє використовувати функції магазину (тобто збережені процедури, функції, що оцінюються за таблицею тощо) у додатках, що використовують Code First падыход та Entity Framework 6.1.1 ( або новіші). Я більш ніж задоволений виправленнями та новими можливостями, які містяться у цій версії.

Детальніше .


Насправді з 4.0 ви можете виконувати SProcs без моделі. Вам потрібно було виконати Raw 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 - це ідентифікатор, що використовує іншу назву.
йогіхостінг

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

Тепер скажемо, що ми хочемо виконати ще одну збережену процедуру з двома параметрами:

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

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

Зауважте, що ми використовуємо іменування на основі індексу для параметрів. Це тому, що Entity Framework буде обробляти ці параметри як об'єкти DbParameter сюди, щоб уникнути будь-яких проблем із введенням SQL.

Сподіваюся, цей приклад допомагає!


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

Оновлено для контексту

Контекст - це примірник класу, як Inherit DbContext, як показано нижче.

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

var Context = new  ApplicationDbContext();

Привіт, я не можу знайти цей Context.Database.SqlQuery <Model>, де я можу це зробити Context.TableName.SqlQuery (ProcName). що задає мені питання
Маршалл

@Marshall, можливо, ви спочатку використовуєте базу даних. будь ласка , перевірте це посилання stackoverflow.com/questions/11792018 / ...
reza.cse08

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

Я виявив, що викликати збережені процедури в Code First підхід не є зручним. Я вважаю за краще використовувати 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();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.