Сначала вставить, обновить и удалить хранимые процедуры кода

Примечание.

Только в EF6 и более поздних версиях. Функции, API и другие возможности, описанные на этой странице, появились в Entity Framework 6. При использовании более ранней версии могут быть неприменимы некоторые или все сведения.

По умолчанию Code First настраивает все сущности для выполнения команд вставки, обновления и удаления с помощью прямого доступа к таблице. Начиная с EF6 можно настроить модель Code First для использования хранимых процедур для некоторых или всех сущностей в модели.

Базовое сопоставление сущностей

Вы можете использовать хранимые процедуры для вставки, обновления и удаления с помощью API Fluent.

modelBuilder
  .Entity<Blog>()
  .MapToStoredProcedures();

Это приведет к тому, что code First будет использовать некоторые соглашения для создания ожидаемой формы хранимых процедур в базе данных.

  • Три хранимых процедуры с именем <type_name>_Insert,< type_name>_Update и <type_name>_Delete (например, Blog_Insert, Blog_Update и Blog_Delete).
  • Имена параметров соответствуют именам свойств.

    Примечание.

    Если вы используете HasColumnName() или атрибут Column для переименования столбца для заданного свойства, то это имя используется для параметров вместо имени свойства.

  • Хранимая процедура вставки будет иметь параметр для каждого свойства, за исключением тех, которые помечены как созданные в качестве хранилища (удостоверение или вычисляемые). Хранимая процедура должна возвращать результирующий набор со столбцом для каждого созданного свойства хранилища.
  • Хранимая процедура обновления будет иметь параметр для каждого свойства, за исключением тех, которые помечены с созданным хранилищем шаблоном computed. Для некоторых маркеров параллелизма требуется параметр исходного значения, см . раздел "Маркеры параллелизма" ниже. Хранимая процедура должна возвращать результирующий набор со столбцом для каждого вычисляемого свойства.
  • Хранимая процедура удаления должна иметь параметр для значения ключа сущности (или нескольких параметров, если сущность имеет составной ключ). Кроме того, процедура удаления должна иметь параметры для любых внешних ключей независимой ассоциации в целевой таблице (связи, которые не имеют соответствующих свойств внешнего ключа, объявленных в сущности). Для некоторых маркеров параллелизма требуется параметр исходного значения, см . раздел "Маркеры параллелизма" ниже.

Использование следующего класса в качестве примера:

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  public string Url { get; set; }  
}

Хранимые процедуры по умолчанию:

CREATE PROCEDURE [dbo].[Blog_Insert]  
  @Name nvarchar(max),  
  @Url nvarchar(max)  
AS  
BEGIN
  INSERT INTO [dbo].[Blogs] ([Name], [Url])
  VALUES (@Name, @Url)

  SELECT SCOPE_IDENTITY() AS BlogId
END
CREATE PROCEDURE [dbo].[Blog_Update]  
  @BlogId int,  
  @Name nvarchar(max),  
  @Url nvarchar(max)  
AS  
  UPDATE [dbo].[Blogs]
  SET [Name] = @Name, [Url] = @Url     
  WHERE BlogId = @BlogId;
CREATE PROCEDURE [dbo].[Blog_Delete]  
  @BlogId int  
AS  
  DELETE FROM [dbo].[Blogs]
  WHERE BlogId = @BlogId

Переопределение значений по умолчанию

Вы можете переопределить часть или все параметры, настроенные по умолчанию.

Имя одной или нескольких хранимых процедур можно изменить. В этом примере переименовывается только хранимая процедура обновления.

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.HasName("modify_blog")));

В этом примере переименовывается все три хранимых процедуры.

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.HasName("modify_blog"))  
     .Delete(d => d.HasName("delete_blog"))  
     .Insert(i => i.HasName("insert_blog")));

В этих примерах вызовы объединяются друг с другом, но можно также использовать лямбда-блочный синтаксис.

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    {  
      s.Update(u => u.HasName("modify_blog"));  
      s.Delete(d => d.HasName("delete_blog"));  
      s.Insert(i => i.HasName("insert_blog"));  
    });

В этом примере параметр для свойства BlogId переименовывается в хранимой процедуре обновления.

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.Parameter(b => b.BlogId, "blog_id")));

Эти вызовы доступны для цепочки и являются компонуемыми. Ниже приведен пример переименования всех трех хранимых процедур и их параметров.

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.HasName("modify_blog")  
                   .Parameter(b => b.BlogId, "blog_id")  
                   .Parameter(b => b.Name, "blog_name")  
                   .Parameter(b => b.Url, "blog_url"))  
     .Delete(d => d.HasName("delete_blog")  
                   .Parameter(b => b.BlogId, "blog_id"))  
     .Insert(i => i.HasName("insert_blog")  
                   .Parameter(b => b.Name, "blog_name")  
                   .Parameter(b => b.Url, "blog_url")));

Вы также можете изменить имя столбцов в результирующем наборе, содержающем созданные базы данных значения.

modelBuilder
  .Entity<Blog>()
  .MapToStoredProcedures(s =>
    s.Insert(i => i.Result(b => b.BlogId, "generated_blog_identity")));
CREATE PROCEDURE [dbo].[Blog_Insert]  
  @Name nvarchar(max),  
  @Url nvarchar(max)  
AS  
BEGIN
  INSERT INTO [dbo].[Blogs] ([Name], [Url])
  VALUES (@Name, @Url)

  SELECT SCOPE_IDENTITY() AS generated_blog_id
END

Связи без внешнего ключа в классе (независимые ассоциации)

Если свойство внешнего ключа включено в определение класса, соответствующий параметр можно переименовать так же, как и любое другое свойство. Если связь существует без свойства внешнего ключа в классе, имя параметра по умолчанию — <navigation_property_name>_<primary_key_name>.

Например, следующие определения классов приводят к тому, что в хранимых процедурах ожидается Blog_BlogId параметр для вставки и обновления записей.

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  public string Url { get; set; }

  public List<Post> Posts { get; set; }  
}  

public class Post  
{  
  public int PostId { get; set; }  
  public string Title { get; set; }  
  public string Content { get; set; }  

  public Blog Blog { get; set; }  
}

Переопределение значений по умолчанию

Параметры внешних ключей, которые не включены в класс, можно изменить, указав путь к свойству первичного ключа методу Parameter.

modelBuilder
  .Entity<Post>()  
  .MapToStoredProcedures(s =>  
    s.Insert(i => i.Parameter(p => p.Blog.BlogId, "blog_id")));

Если у вас нет свойства навигации для зависимой сущности (т. е. нет Post.Blog свойства), можно использовать метод Association для идентификации другого конца связи, а затем настроить параметры, соответствующие каждому из ключевых свойств.

modelBuilder
  .Entity<Post>()  
  .MapToStoredProcedures(s =>  
    s.Insert(i => i.Navigation<Blog>(  
      b => b.Posts,  
      c => c.Parameter(b => b.BlogId, "blog_id"))));

Маркеры параллелизма

Также может потребоваться обновить и удалить хранимые процедуры с параллелизмом:

  • Если сущность содержит маркеры параллелизма, хранимая процедура может при необходимости иметь выходной параметр, возвращающий количество обновленных или удаленных строк (затронутых строк). Такой параметр необходимо настроить с помощью метода RowsAffectedParameter.
    По умолчанию EF использует возвращаемое значение из ExecuteNonQuery для определения количества затронутых строк. Указание затронутого выходного параметра строк полезно при выполнении любой логики в sproc, которая приведет к неправильному значению ExecuteNonQuery (с точки зрения EF) в конце выполнения.
  • Для каждого маркера параллелизма будет параметр с именем <property_name>_Original (например, Timestamp_Original). Это значение будет передано исходное значение этого свойства — значение при запросе из базы данных.
    • Маркеры параллелизма, вычисляемые базой данных , например метки времени, будут иметь только исходный параметр значения.
    • Не вычисляемые свойства, которые задаются как маркеры параллелизма, также будут иметь параметр для нового значения в процедуре обновления. В этом случае используются соглашения об именовании, уже рассмотренные для новых значений. Пример такого токена будет использовать URL-адрес блога в качестве маркера параллелизма, так как это может быть обновлено до нового значения кода (в отличие от маркера метки времени, который обновляется только базой данных).

Это пример класса и обновление хранимой процедуры с маркером параллелизма метки времени.

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  public string Url { get; set; }  
  [Timestamp]
  public byte[] Timestamp { get; set; }
}
CREATE PROCEDURE [dbo].[Blog_Update]  
  @BlogId int,  
  @Name nvarchar(max),  
  @Url nvarchar(max),
  @Timestamp_Original rowversion  
AS  
  UPDATE [dbo].[Blogs]
  SET [Name] = @Name, [Url] = @Url     
  WHERE BlogId = @BlogId AND [Timestamp] = @Timestamp_Original

Ниже приведен пример класса и обновление хранимой процедуры с маркером параллелизма, не вычисляемого.

public class Blog  
{  
  public int BlogId { get; set; }  
  public string Name { get; set; }  
  [ConcurrencyCheck]
  public string Url { get; set; }  
}
CREATE PROCEDURE [dbo].[Blog_Update]  
  @BlogId int,  
  @Name nvarchar(max),  
  @Url nvarchar(max),
  @Url_Original nvarchar(max),
AS  
  UPDATE [dbo].[Blogs]
  SET [Name] = @Name, [Url] = @Url     
  WHERE BlogId = @BlogId AND [Url] = @Url_Original

Переопределение значений по умолчанию

При необходимости можно ввести затронутый параметр строк.

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.RowsAffectedParameter("rows_affected")));

Для вычисленных маркеров параллелизма базы данных , где передается только исходное значение, можно просто использовать стандартный механизм переименования параметров для переименования параметра для исходного значения.

modelBuilder  
  .Entity<Blog>()  
  .MapToStoredProcedures(s =>  
    s.Update(u => u.Parameter(b => b.Timestamp, "blog_timestamp")));

Для маркеров параллелизма, не вычисляемых , где передается исходное и новое значение, можно использовать перегрузку параметра, которая позволяет указать имя каждого параметра.

modelBuilder
 .Entity<Blog>()
 .MapToStoredProcedures(s => s.Update(u => u.Parameter(b => b.Url, "blog_url", "blog_original_url")));

Отношения "многие ко многим"

В этом разделе мы будем использовать следующие классы в качестве примера.

public class Post  
{  
  public int PostId { get; set; }  
  public string Title { get; set; }  
  public string Content { get; set; }  

  public List<Tag> Tags { get; set; }  
}  

public class Tag  
{  
  public int TagId { get; set; }  
  public string TagName { get; set; }  

  public List<Post> Posts { get; set; }  
}

Многие из многих связей можно сопоставить с хранимыми процедурами со следующим синтаксисом.

modelBuilder  
  .Entity<Post>()  
  .HasMany(p => p.Tags)  
  .WithMany(t => t.Posts)  
  .MapToStoredProcedures();

Если другая конфигурация не указана, по умолчанию используется следующая фигура хранимой процедуры.

  • Две хранимые процедуры с именем <type_one><type_two>_Insert и <type_one><type_two>_Delete (например, PostTag_Insert и PostTag_Delete).
  • Параметры будут ключевыми значениями для каждого типа. Имя каждого параметра <type_name>_<property_name> (например, Post_PostId и Tag_TagId).

Ниже приведен пример вставки и обновления хранимых процедур.

CREATE PROCEDURE [dbo].[PostTag_Insert]  
  @Post_PostId int,  
  @Tag_TagId int  
AS  
  INSERT INTO [dbo].[Post_Tags] (Post_PostId, Tag_TagId)   
  VALUES (@Post_PostId, @Tag_TagId)
CREATE PROCEDURE [dbo].[PostTag_Delete]  
  @Post_PostId int,  
  @Tag_TagId int  
AS  
  DELETE FROM [dbo].[Post_Tags]    
  WHERE Post_PostId = @Post_PostId AND Tag_TagId = @Tag_TagId

Переопределение значений по умолчанию

Имена процедур и параметров можно настроить так же, как и хранимые процедуры сущностей.

modelBuilder  
  .Entity<Post>()  
  .HasMany(p => p.Tags)  
  .WithMany(t => t.Posts)  
  .MapToStoredProcedures(s =>  
    s.Insert(i => i.HasName("add_post_tag")  
                   .LeftKeyParameter(p => p.PostId, "post_id")  
                   .RightKeyParameter(t => t.TagId, "tag_id"))  
     .Delete(d => d.HasName("remove_post_tag")  
                   .LeftKeyParameter(p => p.PostId, "post_id")  
                   .RightKeyParameter(t => t.TagId, "tag_id")));