Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
ENDAST EF6 – De funktioner, API:er osv. som beskrivs på den här sidan introducerades i Entity Framework 6. Om du använder en tidigare version gäller inte en del av eller all information.
Modellbaserade konventioner är en avancerad metod för konventionsbaserad modellkonfiguration. I de flesta scenarier bör API:et för den första konventionen för anpassad kod på DbModelBuilder användas. En förståelse av DBModelBuilder-API:et för konventioner rekommenderas innan du använder modellbaserade konventioner.
Modellbaserade konventioner gör det möjligt att skapa konventioner som påverkar egenskaper och tabeller som inte kan konfigureras via standardkonventioner. Exempel på dessa är diskriminerande kolumner i tabell per hierarkimodeller och oberoende associationskolumner.
Skapa en konvention
Det första steget i att skapa en modellbaserad konvention är att välja när konventionen måste tillämpas på modellen i pipelinen. Det finns två typer av modellkonventioner, Konceptuell (C-Space) och Store (S-Space). En C-Space-konvention tillämpas på den modell som programmet bygger, medan en S-Space-konvention tillämpas på den version av modellen som representerar databasen och styr saker som hur automatiskt genererade kolumner namnges.
En modellkonvention är en klass som sträcker sig från antingen IConceptualModelConvention eller IStoreModelConvention. Dessa gränssnitt accepterar båda en allmän typ som kan vara av typen MetadataItem som används för att filtrera den datatyp som konventionen gäller för.
Lägga till en konvention
Modellkonventioner läggs till på samma sätt som vanliga konventionsklasser. I metoden OnModelCreating lägger du till konventionen i listan över konventioner för en modell.
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>();
}
}
En konvention kan också läggas till i förhållande till en annan konvention med hjälp av metoderna Conventions.AddBefore<> eller Conventions.AddAfter<> . Mer information om de konventioner som Entity Framework tillämpar finns i avsnittet anteckningar.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.AddAfter<IdKeyDiscoveryConvention>(new MyModelBasedConvention());
}
Exempel: Konvention om diskriminerande modell
Att byta namn på kolumner som genereras av EF är ett exempel på något som du inte kan göra med de andra konventionernas API:er. Det här är en situation där användning av modellkonventioner är ditt enda alternativ.
Ett exempel på hur du använder en modellbaserad konvention för att konfigurera genererade kolumner är att anpassa hur diskriminerande kolumner namnges. Nedan visas ett exempel på en enkel modellbaserad konvention som byter namn på varje kolumn i modellen med namnet "Discriminator" till "EntityType". Detta inkluderar kolumner som utvecklaren helt enkelt heter "Discriminator". Eftersom kolumnen "Discriminator" är en genererad kolumn måste den köras i S-Space.
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";
}
}
}
Exempel: Allmän IA-namnbyteskonvention
Ett annat mer komplicerat exempel på modellbaserade konventioner i praktiken är att konfigurera hur oberoende associationer (IA) namnges. Det här är en situation där modellkonventioner är tillämpliga eftersom IA:er genereras av EF och inte finns i modellen som DBModelBuilder-API:et kan komma åt.
När EF genererar en IA skapas en kolumn med namnet EntityType_KeyName. För en association med namnet Kund med en nyckelkolumn med namnet CustomerId genererar den till exempel en kolumn med namnet Customer_CustomerId. Följande konvention tar bort tecknet '_' från kolumnnamnet som genereras för 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);
}
}
}
}
Utöka befintliga konventioner
Om du behöver skriva en konvention som liknar en av de konventioner som Entity Framework redan gäller för din modell kan du alltid utöka den konventionen för att undvika att behöva skriva om den från grunden. Ett exempel på detta är att ersätta den befintliga ID-matchningskonventionen med en anpassad. En extra fördel med att åsidosätta nyckelkonventionen är att den åsidosatta metoden anropas endast om ingen nyckel redan har identifierats eller konfigurerats uttryckligen. En lista över konventioner som används av Entity Framework finns här: 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;
}
}
Sedan måste vi lägga till vår nya konvention före den befintliga nyckelkonventionen. När vi har lagt till CustomKeyDiscoveryConvention kan vi ta bort IdKeyDiscoveryConvention. Om vi inte tog bort den befintliga IdKeyDiscoveryConvention skulle den här konventionen fortfarande ha företräde framför ID-identifieringskonventionen eftersom den körs först, men om ingen nyckelegenskap hittas körs "id"-konventionen. Vi ser det här beteendet eftersom varje konvention ser modellen som uppdaterad av den tidigare konventionen (i stället för att arbeta oavhängigt varandra och sedan alla kombineras ihop) så att om till exempel en tidigare konvention uppdaterade ett kolumnnamn för att matcha något av intresse för din anpassade konvention (när namnet aldrig tidigare var av intresse) så gäller det för den kolumnen.
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
En lista över konventioner som för närvarande tillämpas av Entity Framework finns i MSDN-dokumentationen här: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Den här listan hämtas direkt från vår källkod. Källkoden för Entity Framework 6 är tillgänglig på GitHub och många av de konventioner som används av Entity Framework är bra utgångspunkter för anpassade modellbaserade konventioner.