Code First Insert, Update i Delete Stored Procedures

Uwaga

Tylko rozwiązanie EF6 i nowsze wersje — Funkcje, interfejsy API itp. omówione na tej stronie zostały wprowadzone w rozwiązaniu Entity Framework 6. Jeśli korzystasz ze starszej wersji, niektóre lub wszystkie podane informacje nie mają zastosowania.

Domyślnie funkcja Code First konfiguruje wszystkie jednostki w celu wstawiania, aktualizowania i usuwania poleceń przy użyciu bezpośredniego dostępu do tabeli. Począwszy od platformy EF6, możesz skonfigurować model Code First tak, aby używał procedur składowanych dla niektórych lub wszystkich jednostek w modelu.

Mapowanie jednostek podstawowych

Możesz zdecydować się na używanie procedur składowanych do wstawiania, aktualizowania i usuwania przy użyciu interfejsu API Fluent.

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

W ten sposób kod First będzie używać niektórych konwencji w celu utworzenia oczekiwanego kształtu procedur składowanych w bazie danych.

  • Trzy procedury składowane o nazwie <type_name>_Insert,< type_name>_Update i <type_name>_Delete (na przykład Blog_Insert, Blog_Update i Blog_Delete).
  • Nazwy parametrów odpowiadają nazwom właściwości.

    Uwaga

    Jeśli używasz atrybutu HasColumnName() lub atrybutu Kolumna, aby zmienić nazwę kolumny dla danej właściwości, ta nazwa jest używana dla parametrów zamiast nazwy właściwości.

  • Procedura składowana insert będzie zawierać parametr dla każdej właściwości, z wyjątkiem tych oznaczonych jako magazyn wygenerowany (tożsamość lub obliczona). Procedura składowana powinna zwrócić zestaw wyników z kolumną dla każdej wygenerowanej właściwości magazynu.
  • Procedura składowana aktualizacji będzie zawierać parametr dla każdej właściwości, z wyjątkiem tych oznaczonych wzorcem wygenerowanym przez magazyn "Obliczony". Niektóre tokeny współbieżności wymagają parametru dla oryginalnej wartości, zobacz sekcję Tokeny współbieżności poniżej, aby uzyskać szczegółowe informacje. Procedura składowana powinna zwrócić zestaw wyników z kolumną dla każdej obliczonej właściwości.
  • Procedura składowana usuwania powinna mieć parametr dla wartości klucza jednostki (lub wielu parametrów, jeśli jednostka ma klucz złożony). Ponadto procedura usuwania powinna również zawierać parametry dla jakichkolwiek niezależnych kluczy obcych skojarzenia w tabeli docelowej (relacje, które nie mają odpowiednich właściwości klucza obcego zadeklarowanych w jednostce). Niektóre tokeny współbieżności wymagają parametru dla oryginalnej wartości, zobacz sekcję Tokeny współbieżności poniżej, aby uzyskać szczegółowe informacje.

Korzystając z następującej klasy jako przykładu:

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

Domyślne procedury składowane to:

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

Zastępowanie wartości domyślnych

Możesz zastąpić część lub wszystkie skonfigurowane domyślnie częścią.

Możesz zmienić nazwę co najmniej jednej procedury składowanych. W tym przykładzie zmieniono nazwę tylko procedury składowanej aktualizacji.

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

W tym przykładzie zmieniono nazwy wszystkich trzech procedur składowanych.

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

W tych przykładach wywołania są połączone w łańcuch, ale można również użyć składni bloku lambda.

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

W tym przykładzie zmieniono nazwę parametru właściwości BlogId w procedurze składowanej aktualizacji.

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

Te wywołania są możliwe do łączenia łańcuchowego i komponowalne. Oto przykład, który zmienia nazwę wszystkich trzech procedur składowanych i ich parametrów.

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

Możesz również zmienić nazwę kolumn w zestawie wyników zawierającym wygenerowane wartości bazy danych.

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

Relacje bez klucza obcego w klasie (niezależne skojarzenia)

Jeśli właściwość klucza obcego jest uwzględniona w definicji klasy, można zmienić nazwę odpowiedniego parametru w taki sam sposób, jak każda inna właściwość. Jeśli relacja istnieje bez właściwości klucza obcego w klasie, domyślna nazwa parametru to <navigation_property_name>_<primary_key_name>.

Na przykład następujące definicje klas spowodują oczekiwany parametr Blog_BlogId w procedurach składowanych w celu wstawiania i aktualizowania wpisów.

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

Zastępowanie wartości domyślnych

Parametry kluczy obcych, które nie są uwzględnione w klasie, można zmienić, podając ścieżkę do właściwości klucza podstawowego do metody Parameter.

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

Jeśli nie masz właściwości nawigacji dla jednostki zależnej (tj. nie Post.Blog właściwości), możesz użyć metody Skojarzenie, aby zidentyfikować drugi koniec relacji, a następnie skonfigurować parametry odpowiadające każdej właściwości klucza.

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

Tokeny współbieżności

Aktualizowanie i usuwanie procedur składowanych może również wymagać obsługi współbieżności:

  • Jeśli jednostka zawiera tokeny współbieżności, procedura składowana może opcjonalnie mieć parametr wyjściowy, który zwraca liczbę wierszy zaktualizowanych/usuniętych (wiersze, których dotyczy problem). Taki parametr należy skonfigurować przy użyciu metody RowsAffectedParameter.
    Domyślnie program EF używa wartości zwracanej z executeNonQuery, aby określić liczbę wierszy, których dotyczy problem. Określenie parametru wyjściowego, którego dotyczy problem, wierszy jest przydatne, jeśli wykonasz dowolną logikę w sproc, co spowoduje zwrócenie wartości ExecuteNonQuery jest niepoprawne (z perspektywy ef) na końcu wykonywania.
  • Dla każdego tokenu współbieżności będzie dostępny parametr o nazwie <property_name>_Original (na przykład Timestamp_Original ). Zostanie przekazana oryginalna wartość tej właściwości — wartość podczas wykonywania zapytań z bazy danych.
    • Tokeny współbieżności obliczane przez bazę danych — takie jak znaczniki czasu — będą miały tylko oryginalny parametr wartości.
    • Właściwości nieobliczone, które są ustawione jako tokeny współbieżności, również będą miały parametr dla nowej wartości w procedurze aktualizacji. Korzysta to z konwencji nazewnictwa, które zostały już omówione dla nowych wartości. Przykładem takiego tokenu byłoby użycie adresu URL bloga jako tokenu współbieżności. Nowa wartość jest wymagana, ponieważ można ją zaktualizować do nowej wartości za pomocą kodu (w przeciwieństwie do tokenu znacznika czasu, który jest aktualizowany tylko przez bazę danych).

Jest to przykładowa klasa i aktualizacja procedury składowanej przy użyciu tokenu współbieżności sygnatury czasowej.

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

Oto przykładowa klasa i aktualizacja procedury składowanej przy użyciu tokenu współbieżności bez obliczeń.

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

Zastępowanie wartości domyślnych

Opcjonalnie można wprowadzić wiersze, których dotyczy problem parametru.

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

W przypadku tokenów współbieżności obliczanej bazy danych — gdzie przekazywana jest tylko oryginalna wartość — można po prostu użyć mechanizmu zmiany nazwy parametru standardowego, aby zmienić nazwę parametru dla oryginalnej wartości.

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

W przypadku tokenów współbieżności nie obliczonych — w przypadku których przekazywana jest zarówno oryginalna, jak i nowa wartość — można użyć przeciążenia parametru, które umożliwia podanie nazwy dla każdego parametru.

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

Wiele do wielu relacji

W tej sekcji użyjemy następujących klas jako przykładu.

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

Wiele do wielu relacji można mapować na procedury składowane przy użyciu następującej składni.

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

Jeśli nie zostanie podana żadna inna konfiguracja, domyślnie jest używany następujący kształt procedury składowanej.

  • Dwie procedury składowane o nazwie <type_one type_two><>_Insert i< type_one type_two><>_Delete (na przykład PostTag_Insert i PostTag_Delete).
  • Parametry będą wartościami klucza dla każdego typu. Nazwa każdego parametru type_name<>_<property_name> (na przykład Post_PostId i Tag_TagId).

Oto przykładowe procedury składowane wstawiania i aktualizowania.

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

Zastępowanie wartości domyślnych

Procedury i nazwy parametrów można skonfigurować w podobny sposób do procedur składowanych jednostki.

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