Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Apenas a partir do EF6 - As funcionalidades, APIs, etc. discutidas nesta página foram introduzidas no Entity Framework 6. Se estiver a usar uma versão anterior, parte ou toda a informação não se aplica.
As convenções baseadas em modelos são um método avançado de configuração baseada em convenções. Para a maioria dos cenários, deve ser usada a API Custom Code First Convention no DbModelBuilder . Recomenda-se compreender a API DbModelBuilder para convenções antes de usar convenções baseadas em modelos.
As convenções baseadas em modelos permitem a criação de convenções que afetam propriedades e tabelas que não são configuráveis através de convenções padrão. Exemplos desses são as colunas discriminadoras em modelos de tabela por hierarquia e colunas de associações independentes.
Criação de uma Convenção
O primeiro passo para criar uma convenção baseada em modelos é escolher quando, no pipeline, a convenção deve ser aplicada ao modelo. Existem dois tipos de convenções de modelo, Conceptual (C-Space) e Store (S-Space). Uma convenção C-Space é aplicada ao modelo que a aplicação constrói, enquanto uma convenção S-Space é aplicada à versão do modelo que representa a base de dados e controla aspetos como o nome das colunas geradas automaticamente.
Uma convenção de modelo é uma classe que se estende a partir de IConceptualModelConvention ou IStoreModelConvention. Estas interfaces aceitam ambos um tipo genérico que pode ser do tipo MetadataItem, que é usado para filtrar o tipo de dado a que a convenção se aplica.
Adição de uma Convenção
As convenções modelo são adicionadas da mesma forma que as classes de convenções regulares. No método OnModelCreate , adicione a convenção à lista de convenções para um modelo.
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
public class BlogContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add<MyModelBasedConvention>();
}
}
Uma convenção também pode ser adicionada em relação a outra convenção usando os métodos Conventions.AddBefore<> ou Conventions.AddAfter<>. Para mais informações sobre as convenções que o Entity Framework aplica, consulte a secção de notas.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.AddAfter<IdKeyDiscoveryConvention>(new MyModelBasedConvention());
}
Exemplo: Convenção do Modelo Discriminador
Renomear colunas geradas pelo EF é um exemplo de algo que não se pode fazer com as APIs de outras convenções. Esta é uma situação em que usar convenções de modelos é a tua única opção.
Um exemplo de como usar uma convenção baseada em modelos para configurar colunas geradas é personalizar a forma como as colunas discriminadoras são nomeadas. Abaixo está um exemplo de uma convenção simples baseada em modelos que renomeia cada coluna do modelo chamada "Discriminador" para "EntityType". Isto inclui colunas que o programador simplesmente denominou "Discriminador". Como a coluna "Discriminador" é uma coluna gerada, esta precisa de ser executada no Espaço S.
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
class DiscriminatorRenamingConvention : IStoreModelConvention<EdmProperty>
{
public void Apply(EdmProperty property, DbModel model)
{
if (property.Name == "Discriminator")
{
property.Name = "EntityType";
}
}
}
Exemplo: Convenção Geral de Renomeação da IA
Outro exemplo mais complicado de convenções baseadas em modelos em ação é configurar a forma como as Associações Independentes (IAs) são nomeadas. Esta é uma situação em que as convenções do Modelo são aplicáveis porque as IAs são geradas pelo EF e não estão presentes no modelo ao qual a API do DbModelBuilder pode aceder.
Quando o EF gera um IA, cria uma coluna chamada EntityType_KeyName. Por exemplo, para uma associação chamada Customer com uma coluna chave chamada CustomerId, geraria uma coluna chamada Customer_CustomerId. A convenção seguinte remove o carácter '_' do nome da coluna que é gerado para a IA.
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
// Provides a convention for fixing the independent association (IA) foreign key column names.
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType association, DbModel model)
{
// Identify ForeignKey properties (including IAs)
if (association.IsForeignKey)
{
// rename FK columns
var constraint = association.Constraint;
if (DoPropertiesHaveDefaultNames(constraint.FromProperties, constraint.ToRole.Name, constraint.ToProperties))
{
NormalizeForeignKeyProperties(constraint.FromProperties);
}
if (DoPropertiesHaveDefaultNames(constraint.ToProperties, constraint.FromRole.Name, constraint.FromProperties))
{
NormalizeForeignKeyProperties(constraint.ToProperties);
}
}
}
private bool DoPropertiesHaveDefaultNames(ReadOnlyMetadataCollection<EdmProperty> properties, string roleName, ReadOnlyMetadataCollection<EdmProperty> otherEndProperties)
{
if (properties.Count != otherEndProperties.Count)
{
return false;
}
for (int i = 0; i < properties.Count; ++i)
{
if (!properties[i].Name.EndsWith("_" + otherEndProperties[i].Name))
{
return false;
}
}
return true;
}
private void NormalizeForeignKeyProperties(ReadOnlyMetadataCollection<EdmProperty> properties)
{
for (int i = 0; i < properties.Count; ++i)
{
int underscoreIndex = properties[i].Name.IndexOf('_');
if (underscoreIndex > 0)
{
properties[i].Name = properties[i].Name.Remove(underscoreIndex, 1);
}
}
}
}
Extensão das Convenções Existentes
Se precisares de escrever uma convenção semelhante a uma das convenções que o Entity Framework já aplica ao teu modelo, podes sempre estender essa convenção para evitar ter de a reescrever do zero. Um exemplo disto é substituir a convenção existente de correspondência de ID por uma personalizada. Um benefício adicional de substituir a convenção de chaves é que o método substituído só será chamado se não houver uma chave já detetada ou configurada explicitamente. Uma lista de convenções utilizadas pelo Entity Framework está disponível aqui: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx.
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
// Convention to detect primary key properties.
// Recognized naming patterns in order of precedence are:
// 1. 'Key'
// 2. [type name]Key
// Primary key detection is case insensitive.
public class CustomKeyDiscoveryConvention : KeyDiscoveryConvention
{
private const string Id = "Key";
protected override IEnumerable<EdmProperty> MatchKeyProperty(
EntityType entityType, IEnumerable<EdmProperty> primitiveProperties)
{
Debug.Assert(entityType != null);
Debug.Assert(primitiveProperties != null);
var matches = primitiveProperties
.Where(p => Id.Equals(p.Name, StringComparison.OrdinalIgnoreCase));
if (!matches.Any())
{
matches = primitiveProperties
.Where(p => (entityType.Name + Id).Equals(p.Name, StringComparison.OrdinalIgnoreCase));
}
// If the number of matches is more than one, then multiple properties matched differing only by
// case--for example, "Key" and "key".
if (matches.Count() > 1)
{
throw new InvalidOperationException("Multiple properties match the key convention");
}
return matches;
}
}
Depois precisamos de adicionar a nossa nova convenção antes da convenção chave existente. Depois de adicionarmos a CustomKeyDiscoveryConvention, podemos remover a IdKeyDiscoveryConvention. Se não removêssemos a IdKeyDiscoveryConvention existente, esta convenção continuaria a ter precedência sobre a convenção de descoberta do Id, uma vez que é executada primeiro, mas no caso em que nenhuma propriedade "chave" seja encontrada, a convenção "id" será executada. Vemos este comportamento porque cada convenção vê o modelo como atualizado pela convenção anterior (em vez de operar nele de forma independente e tudo combinado), de modo que, por exemplo, se uma convenção anterior atualizou o nome de uma coluna para corresponder a algo de interesse para a sua convenção personalizada (quando antes disso o nome não era de interesse), então isso se aplicará a essa coluna.
public class BlogContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.AddBefore<IdKeyDiscoveryConvention>(new CustomKeyDiscoveryConvention());
modelBuilder.Conventions.Remove<IdKeyDiscoveryConvention>();
}
}
Notes
Uma lista de convenções atualmente aplicadas pelo Entity Framework está disponível na documentação da MSDN aqui: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Esta lista é retirada diretamente do nosso código-fonte. O código-fonte do Entity Framework 6 está disponível no GitHub e muitas das convenções usadas pelo Entity Framework são bons pontos de partida para convenções personalizadas baseadas em modelos.