Соглашения на основе моделей
Примечание.
Только в EF6 и более поздних версиях. Функции, API и другие возможности, описанные на этой странице, появились в Entity Framework 6. При использовании более ранней версии могут быть неприменимы некоторые или все сведения.
Соглашения на основе моделей — это расширенный метод конфигурации модели на основе соглашений. Для большинства сценариев следует использовать API первого соглашения пользовательского кода в DbModelBuilder . Перед использованием соглашений рекомендуется понимать API DbModelBuilder для соглашений.
Соглашения на основе моделей позволяют создавать соглашения, влияющие на свойства и таблицы, которые не настраиваются с помощью стандартных соглашений. Примерами этих столбцов являются дискриминационные столбцы в таблице для каждой модели иерархии и столбцы Независимой ассоциации.
Создание соглашения
Первым шагом при создании соглашения на основе модели является выбор того, когда в конвейере необходимо применить соглашение к модели. Существует два типа соглашений о модели, концептуальные (C-Space) и Store (S-Space). Соглашение C-Space применяется к модели, созданной приложением, в то время как соглашение S-Space применяется к версии модели, представляющей базу данных и управляет такими элементами, как автоматически созданные столбцы.
Соглашение модели — это класс, который расширяется от IConceptualModelConvention или IStoreModelConvention. Эти интерфейсы принимают универсальный тип, который может быть типом MetadataItem, который используется для фильтрации типа данных, к которому применяется соглашение.
Добавление соглашения
Соглашения модели добавляются так же, как и обычные классы соглашений. В методе OnModelCreating добавьте соглашение в список соглашений для модели.
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>();
}
}
Соглашение также можно добавить в отношении другого соглашения с помощью методов Conventions.AddBefore<> или Conventions.AddAfter<> . Дополнительные сведения о соглашениях, применимых Entity Framework, см. в разделе заметок.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.AddAfter<IdKeyDiscoveryConvention>(new MyModelBasedConvention());
}
Пример: Соглашение о дискриминационных модельх
Переименование столбцов, созданных EF, является примером того, что нельзя сделать с другими API-интерфейсами соглашений. Это ситуация, когда использование соглашений о модели является единственным вариантом.
Пример использования соглашения на основе модели для настройки созданных столбцов — настройка способа именования дискриминационных столбцов. Ниже приведен пример простого соглашения на основе модели, которое переименовывает каждый столбец модели с именем "Дискриминационный" на EntityType. К ним относятся столбцы, которые разработчик просто назвал "Дискриминатор". Так как столбец "Дискриминатор" представляет собой созданный столбец, который должен выполняться в 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";
}
}
}
Пример: Общая конвенция об переименовании IA
Еще одним более сложным примером соглашений на основе моделей в действии является настройка способа именования независимых ассоциаций (IAs). Это ситуация, когда применимы соглашения о модели, так как IAs создаются EF и не присутствуют в модели, к которой может получить доступ API DbModelBuilder.
При создании IA EF создается столбец с именем EntityType_KeyName. Например, для сопоставления с именем Customer с ключевым столбцом CustomerId он создаст столбец с именем Customer_CustomerId. В следующем соглашении символ "_" удаляется из имени столбца, созданного для 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);
}
}
}
}
Расширение существующих соглашений
Если вам нужно написать соглашение, аналогичное одному из соглашений, которые Entity Framework уже применяется к модели, вы всегда можете расширить это соглашение, чтобы избежать необходимости переписать его с нуля. Примером этого является замена существующего соглашения о сопоставлении идентификаторов с пользовательским. Дополнительное преимущество переопределения соглашения о ключе заключается в том, что переопределенный метод вызывается только в том случае, если ключ уже обнаружен или явно настроен. Список соглашений, используемых Entity Framework, доступен здесь: 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;
}
}
Затем необходимо добавить новое соглашение до существующего ключевого соглашения. После добавления CustomKeyDiscoveryConvention можно удалить IdKeyDiscoveryConvention. Если мы не удалили существующее соглашение IdKeyDiscoveryConvention, это соглашение по-прежнему будет иметь приоритет над соглашением об обнаружении идентификаторов, так как оно выполняется первым, но в случае, если свойство key не найдено, соглашение "id" будет выполняться. Мы видим это поведение, так как каждое соглашение видит модель как обновленную предыдущей конвенцией (вместо того, чтобы работать на ней независимо и все объединены вместе), чтобы, например, предыдущее соглашение обновило имя столбца, чтобы оно соответствовало своему пользовательскому соглашению (когда до этого имя не было интересом), то оно будет применяться к этому столбцу.
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>();
}
}
Примечания.
Список соглашений, которые в настоящее время применяются Entity Framework, доступен в документации MSDN: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx Этот список извлекается непосредственно из исходного кода. Исходный код entity Framework 6 доступен на сайте GitHub , и многие соглашения, используемые Entity Framework, являются хорошими отправными точками для соглашений на основе пользовательских моделей.