Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Annotazioni
Solo EF6 e versioni successive : le funzionalità, le API e così via descritte in questa pagina sono state introdotte in Entity Framework 6. Se si usa una versione precedente, alcune o tutte le informazioni non si applicano.
Le convenzioni basate su modello sono un metodo avanzato di configurazione del modello basato su convenzioni. Per la maggior parte degli scenari è consigliabile usare l'API Code First Convention personalizzata in DbModelBuilder . È consigliabile comprendere l'API DbModelBuilder per le convenzioni prima di usare le convenzioni basate su modello.
Le convenzioni basate su modello consentono la creazione di convenzioni che influiscono su proprietà e tabelle che non sono configurabili tramite convenzioni standard. Esempi di queste sono colonne discriminanti nei modelli tabella per gerarchia e colonne di associazione indipendente.
Creazione di una convenzione
Il primo passaggio per la creazione di una convenzione basata su modello consiste nel scegliere quando nella pipeline la convenzione deve essere applicata al modello. Esistono due tipi di convenzioni di modello, Conceptual (C-Space) e Store (S-Space). Una convenzione C-Space viene applicata al modello compilato dall'applicazione, mentre una convenzione S-Space viene applicata alla versione del modello che rappresenta il database e controlla elementi quali la modalità di denominazione delle colonne generate automaticamente.
Una convenzione di modello è una classe che si estende da IConceptualModelConvention o IStoreModelConvention. Queste interfacce accettano entrambi un tipo generico che può essere di tipo MetadataItem che viene usato per filtrare il tipo di dati a cui si applica la convenzione.
Aggiunta di una convenzione
Le convenzioni dei modelli vengono aggiunte allo stesso modo delle normali classi di convenzioni. Nel metodo OnModelCreating aggiungere la convenzione all'elenco di convenzioni per un modello.
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>();
}
}
È anche possibile aggiungere una convenzione in relazione a un'altra convenzione usando i metodi Conventions.AddBefore<> o Conventions.AddAfter<> . Per altre informazioni sulle convenzioni che Entity Framework applica, vedere la sezione note.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.AddAfter<IdKeyDiscoveryConvention>(new MyModelBasedConvention());
}
Esempio: Convenzione del modello discriminatorio
La ridenominazione delle colonne generate da EF è un esempio di un'operazione che non è possibile eseguire con le altre API delle convenzioni. Si tratta di una situazione in cui l'uso delle convenzioni del modello è l'unica opzione.
Un esempio di come usare una convenzione basata su modello per configurare le colonne generate consiste nel personalizzare il modo in cui vengono denominate le colonne discriminatorie. Di seguito è riportato un esempio di una convenzione semplice basata su modello che rinomina ogni colonna del modello denominata "Discriminator" in "EntityType". Sono incluse le colonne che lo sviluppatore ha semplicemente denominato "Discriminator". Poiché la colonna "Discriminator" è una colonna generata, questa operazione deve essere eseguita nello spazio 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";
}
}
}
Esempio: Convenzione di ridenominazione IA generale
Un altro esempio più complesso di convenzioni basate su modelli in azione consiste nel configurare la modalità di denominazione delle associazioni indipendenti . Si tratta di una situazione in cui le convenzioni del modello sono pertinenti perché le intelligenze artificiali vengono generate da EF e non esistono nel modello a cui l'API del DbModelBuilder può accedere.
Quando EF genera un IA, crea una colonna denominata EntityType_KeyName. Ad esempio, per un'associazione denominata Customer con una colonna chiave denominata CustomerId, verrà generata una colonna denominata Customer_CustomerId. La convenzione seguente rimuove il carattere '_' dal nome della colonna generato per L'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);
}
}
}
}
Estensione delle convenzioni esistenti
Se è necessario scrivere una convenzione simile a una delle convenzioni già applicabili a Entity Framework al modello, è sempre possibile estendere tale convenzione per evitare di dover riscriverla da zero. Un esempio consiste nel sostituire la convenzione di corrispondenza ID esistente con una convenzione personalizzata. Un ulteriore vantaggio per eseguire l'override della convenzione di chiave consiste nel fatto che il metodo sottoposto a override verrà chiamato solo se non è già stata rilevata o configurata in modo esplicito alcuna chiave. Un elenco di convenzioni usate da Entity Framework è disponibile qui: 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;
}
}
È quindi necessario aggiungere la nuova convenzione prima della convenzione chiave esistente. Dopo aver aggiunto CustomKeyDiscoveryConvention, è possibile rimuovere IdKeyDiscoveryConvention. Se non è stata rimossa la convenzione IdKeyDiscoveryConvention esistente, questa convenzione avrà comunque la precedenza sulla convenzione di individuazione id perché viene eseguita per la prima volta, ma nel caso in cui non venga trovata alcuna proprietà "chiave", verrà eseguita la convenzione "id". Osserviamo questo comportamento perché ogni convenzione vede il modello come aggiornato dalla convenzione precedente (anziché operare su di esso in modo indipendente e tutti combinati) in modo che, se ad esempio, una convenzione precedente ha aggiornato un nome di colonna in modo che corrisponda a qualcosa di interessante alla tua convenzione personalizzata (quando prima il nome non destava interesse), allora si applicherà a quella colonna.
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>();
}
}
Note
Un elenco di convenzioni attualmente applicate da Entity Framework è disponibile nella documentazione MSDN qui: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Questo elenco viene estratto direttamente dal codice sorgente. Il codice sorgente per Entity Framework 6 è disponibile in GitHub e molte delle convenzioni usate da Entity Framework sono ottimi punti di partenza per le convenzioni basate su modelli personalizzati.