Как вызвать хранимую процедуру в Entity Framework 6 (Code-First)?

260

Я очень новичок в 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

Хранимые процедуры с несколькими наборами результатов

Alborz
источник
2
Спасибо @Alborz. Можете ли вы предоставить мне несколько ссылок, касающихся различных реализаций хранимых процедур в Entity Framework 6 Code First. Я искал везде в Интернете, но не получил ни одной статьи, где я мог бы напрямую вызывать хранимую процедуру для параметров IN и OUT. Спасибо за ваше драгоценное время.
Яан
2
Эта статья может быть полезна blogs.msdn.com/b/diego/archive/2012/01/10/…
Alborz
8
Это не похоже на работу с параметрами. Кажется, необходимо явно перечислить параметры как часть запроса.
Марк
6
Да, вам нужно указать параметры как часть запроса - "storedProcedureName @param1, @param2". Также тип paramsесть System.Data.SqlClient.SqlParameter[].
Оппа 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
51

Я решил это с 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 для вашей хранимой процедуры - в противном случае, как вы обнаружили, они имеют длину всего один символ.
Дейв W
@Mahdighafoorian Это очень полезный ответ, большое спасибо! :)
Коменгем
Этот синтаксис не требует изменения порядка параметров SProc, другими словами, Порядкового позиционирования.
GoldBishop
21

Используя ваш пример, вот два способа сделать это:

1 - Использовать отображение хранимых процедур

Обратите внимание, что этот код будет работать с отображением или без него. Если вы отключите отображение объекта, EF сгенерирует оператор вставки + выбора.

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
@edtruant dbContext действительно отслеживает изменения. Чтобы проверить, я посмотрел на db. <DbSet> .Count () до и после оператора вставки. В обоих методах количество увеличилось на единицу. Для полноты я добавил альтернативный метод к примеру.
Брайан Вандер Платс
1
Я не вижу ссылки на хранимую процедуру в первом примере.
xr280xr
2
@ xr280xr ссылка на insert_department указана в выражении modelBuilder в вопросе OP. Это является преимуществом для сопоставления вещей таким образом, потому что он эффективно работает так же, как если бы вы позволяли EF генерировать операторы вставки / обновления / удаления
Брайан Вандер Платс
15

Вы используете, MapToStoredProcedures()что означает, что вы отображаете свои сущности на хранимые процедуры, при этом вам нужно отказаться от того факта, что есть хранимая процедура, и использовать ее contextкак обычно. Как то так ( записано в браузере, поэтому не проверено )

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

Если все, что вы действительно пытаетесь сделать, это вызвать хранимую процедуру напрямую, используйте SqlQuery

qujck
источник
2
Спасибо Qujck. Но я хочу использовать хранимую процедуру. Я дал только пример кода для удобного для понимания.
Яан
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 только при выполнении Database First. Были некоторые обходные пути, которые позволяли вызывать функции магазина в приложениях Code First, но вы все еще не могли использовать TVF в запросах Linq, что было одним из самых больших ограничений. В EF 6.1 API-интерфейс отображения стал общедоступным, что (наряду с некоторыми дополнительными настройками) позволило использовать функции хранилища в приложениях Code First.

Читать далее

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

Подробнее .

Pawel
источник
На самом деле, начиная с 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();

Обновлено для контекста

Контекст является экземпляром класса, который наследует DbContext, как показано ниже.

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

var Context = new  ApplicationDbContext();
reza.cse08
источник
Привет, я не могу найти этот 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();
Awais
источник
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();
trueboroda
источник
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);
}
IngoB
источник
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();
        }
mattylantz
источник
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 Framework Code Первый подход:

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();
Хари Лаккакула
источник