Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
Somente EF6– os recursos, as APIs etc. discutidos nesta página foram introduzidos no o Entity Framework 6. Se você estiver usando uma versão anterior, algumas ou todas as informações não se aplicarão.
Por padrão, o Code First configurará todas as entidades para executar comandos de inserção, atualização e exclusão usando o acesso direto à tabela. A partir do EF6, você pode configurar seu modelo Code First para usar procedimentos armazenados para algumas ou todas as entidades em seu modelo.
Mapeamento básico de entidades
Você pode optar por usar procedimentos armazenados para inserir, atualizar e excluir usando a API fluente.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures();
Isso fará com que o Code First use algumas convenções para criar a forma esperada dos procedimentos armazenados no banco de dados.
- Três procedimentos armazenados denominados <type_name>_Insert, <type_name>_Update e <type_name>_Delete (por exemplo, Blog_Insert, Blog_Update e Blog_Delete).
- Os nomes dos parâmetros correspondem aos nomes das propriedades.
Observação
Se você usar HasColumnName() ou o atributo Column para renomear a coluna para uma determinada propriedade, esse nome será usado para parâmetros em vez do nome da propriedade.
- O procedimento armazenado de inserção terá um parâmetro para cada propriedade, exceto para aquelas marcadas como geradas automaticamente (identidade ou computadas). O procedimento armazenado deve retornar um conjunto de resultados com uma coluna para cada propriedade gerada pelo repositório.
- O procedimento armazenado de atualização terá um parâmetro para cada propriedade, exceto aqueles marcados com um padrão gerado pelo repositório de "Computado". Alguns tokens de simultaneidade exigem um parâmetro para o valor original, consulte a seção Tokens de Simultaneidade abaixo para obter detalhes. O procedimento armazenado deve retornar um conjunto de resultados com uma coluna para cada propriedade computada.
- O procedimento armazenado de exclusão deve ter um parâmetro para o valor de chave da entidade (ou vários parâmetros se a entidade tiver uma chave composta). Além disso, o procedimento de exclusão também deve ter parâmetros para qualquer chave estrangeira de associação independente na tabela de destino (relações que não têm propriedades de chave estrangeira correspondentes declaradas na entidade). Alguns tokens de simultaneidade exigem um parâmetro para o valor original, consulte a seção Tokens de Simultaneidade abaixo para obter detalhes.
Usando a seguinte classe como exemplo:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
}
Os procedimentos armazenados padrão seriam:
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
Sobrescrevendo os padrões padrão
Você pode substituir parte ou tudo o que foi configurado por padrão.
Você pode alterar o nome de um ou mais procedimentos armazenados. Este exemplo renomeia apenas o procedimento armazenado de atualização.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
s.Update(u => u.HasName("modify_blog")));
Este exemplo renomeia todos os três procedimentos armazenados.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
s.Update(u => u.HasName("modify_blog"))
.Delete(d => d.HasName("delete_blog"))
.Insert(i => i.HasName("insert_blog")));
Nestes exemplos, as chamadas são encadeadas juntas, mas você também pode usar a sintaxe de bloco 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"));
});
Este exemplo renomeia o parâmetro da propriedade BlogId no procedimento armazenado de atualização.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
s.Update(u => u.Parameter(b => b.BlogId, "blog_id")));
Essas chamadas são encadeáveis e combináveis. Aqui está um exemplo que renomeia todos os três procedimentos armazenados e seus parâmetros.
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")));
Você também pode alterar o nome das colunas no conjunto de resultados que contém valores gerados pelo banco de dados.
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
Relações sem uma chave estrangeira na classe (associações independentes)
Quando uma propriedade de chave estrangeira é incluída na definição de classe, o parâmetro correspondente pode ser renomeado da mesma forma que qualquer outra propriedade. Quando existe uma relação sem uma propriedade de chave estrangeira na classe, o nome do parâmetro padrão é <navigation_property_name>_<primary_key_name>.
Por exemplo, as definições de classe a seguir resultariam em um parâmetro Blog_BlogId sendo esperado nos procedimentos armazenados para inserir e atualizar Postagens.
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; }
}
Sobrescrevendo os padrões padrão
Você pode alterar parâmetros para chaves estrangeiras que não estão incluídas na classe fornecendo o caminho para a propriedade de chave primária para o método Parameter.
modelBuilder
.Entity<Post>()
.MapToStoredProcedures(s =>
s.Insert(i => i.Parameter(p => p.Blog.BlogId, "blog_id")));
Se você não tiver uma propriedade de navegação na entidade dependente (ou seja, nenhuma propriedade Post.Blog), poderá usar o método Association para identificar a outra extremidade da relação e, em seguida, configurar os parâmetros que correspondem a cada uma das propriedades de chave.
modelBuilder
.Entity<Post>()
.MapToStoredProcedures(s =>
s.Insert(i => i.Navigation<Blog>(
b => b.Posts,
c => c.Parameter(b => b.BlogId, "blog_id"))));
Tokens de concorrência
Atualizar e excluir procedimentos armazenados também pode precisar lidar com simultaneidade:
- Se a entidade contiver tokens de simultaneidade, o procedimento armazenado poderá ter opcionalmente um parâmetro de saída que retorna o número de linhas atualizadas/excluídas (linhas afetadas). Esse parâmetro deve ser configurado usando o método RowsAffectedParameter.
Por padrão, o EF usa o valor retornado de ExecuteNonQuery para determinar quantas linhas foram afetadas. Especificar um parâmetro de saída afetado de linhas será útil se você executar qualquer lógica em seu sproc que resulte no valor retornado de ExecuteNonQuery incorreto (da perspectiva do EF) no final da execução. - Para cada token de simultaneidade, haverá um parâmetro chamado <property_name>_Original (por exemplo, Timestamp_Original). Isso passará o valor original dessa propriedade – o valor quando consultado no banco de dados.
- Tokens de simultaneidade computados pelo banco de dados – como carimbos de data/hora – terão apenas um parâmetro de valor original.
- As propriedades não computadas definidas como tokens de simultaneidade também terão um parâmetro para o novo valor no procedimento de atualização. Isso usa as convenções de nomenclatura já discutidas para novos valores. Um exemplo desse token seria usar a URL de um blog como token de concorrência. O novo valor é necessário porque ele pode ser atualizado pelo seu código para um novo valor (ao contrário de um token de carimbo de data/hora, que só é atualizado pelo banco de dados).
Este é um exemplo de classe e de procedimento armazenado de atualização com um token de concorrência de marcação temporal.
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
Aqui está uma classe de exemplo e um procedimento armazenado de atualização com token de simultaneidade não calculado.
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
Sobrescrevendo os padrões
Opcionalmente, você pode introduzir um parâmetro afetado por linhas.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
s.Update(u => u.RowsAffectedParameter("rows_affected")));
Para tokens de concorrência gerados pelo banco de dados – nos quais apenas o valor original é passado – você pode usar o mecanismo padrão de renomeação de parâmetro para renomear o parâmetro para o valor original.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
s.Update(u => u.Parameter(b => b.Timestamp, "blog_timestamp")));
Para tokens de simultaneidade não computados – onde tanto o valor original quanto o novo são passados – você pode usar uma sobrecarga do parâmetro que permite fornecer um nome para cada parâmetro.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s => s.Update(u => u.Parameter(b => b.Url, "blog_url", "blog_original_url")));
Muitos para muitos relacionamentos
Usaremos as seguintes classes como exemplo nesta seção.
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; }
}
Muitos ou muitos relacionamentos podem ser mapeados para procedimentos armazenados com a sintaxe a seguir.
modelBuilder
.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(t => t.Posts)
.MapToStoredProcedures();
Se nenhuma outra configuração for fornecida, a seguinte forma de procedimento armazenado será usada por padrão.
- Dois procedimentos armazenados chamados <type_one><type_two>_Insert e <type_one><type_two>_Delete (por exemplo, PostTag_Insert e PostTag_Delete).
- Os parâmetros serão os valores de chave para cada tipo. O nome de cada parâmetro como <type_name>_<property_name> (por exemplo, Post_PostId e Tag_TagId).
Aqui estão exemplos de procedimentos armazenados de inserção e atualização.
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
Sobrescrevendo os padrões padrão
Os nomes de procedimento e parâmetro podem ser configurados de maneira semelhante aos procedimentos de armazenamento de entidade.
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")));