Конвенции Code First

Code First позволяет описать модель с помощью классов C# или Visual Basic .NET. Базовая форма модели обнаруживается с помощью стандартов. Соглашения — это наборы правил, которые используются для автоматической настройки концептуальной модели на основе определений классов при работе с кодом First. Соглашения определяются в пространстве имен System.Data.Entity.ModelConfiguration.Conventions.

Вы можете дополнительно настроить модель с помощью аннотаций данных или функционального API. Приоритет присваивается конфигурации через флюентный API, затем идут аннотации данных, после этого — соглашения. Дополнительные сведения см. в заметках к данным, API Fluent — связи, API Fluent — Типы и свойства и API Fluentс VB.NET.

Подробный список соглашений Code First доступен в документации по API. В этом разделе представлен обзор соглашений, используемых code First.

Обнаружение типов

При использовании разработки Code First вы обычно начинаете с написания классов .NET Framework, определяющих концептуальную модель (домен). Помимо определения классов, необходимо также сообщить DbContext о типах, которые необходимо включить в модель. Для этого необходимо определить класс контекста, производный от DbContext и предоставляющий свойства DbSet для типов, которые вы хотите быть частью модели. Code First будет включать эти типы, а также будет извлекать любые ссылочные типы, даже если указанные типы определены в другой сборке.

Если типы участвуют в иерархии наследования, достаточно определить свойство DbSet для базового класса, а производные типы будут автоматически включены, если они находятся в той же сборке, что и базовый класс.

В следующем примере существует только одно свойство DbSet , определенное в классе SchoolEntities (Departments). Код сначала использует это свойство для обнаружения и извлечения всех ссылочных типов.

public class SchoolEntities : DbContext
{
    public DbSet<Department> Departments { get; set; }
}

public class Department
{
    // Primary key
    public int DepartmentID { get; set; }
    public string Name { get; set; }

    // Navigation property
    public virtual ICollection<Course> Courses { get; set; }
}

public class Course
{
    // Primary key
    public int CourseID { get; set; }

    public string Title { get; set; }
    public int Credits { get; set; }

    // Foreign key
    public int DepartmentID { get; set; }

    // Navigation properties
    public virtual Department Department { get; set; }
}

public partial class OnlineCourse : Course
{
    public string URL { get; set; }
}

public partial class OnsiteCourse : Course
{
    public string Location { get; set; }
    public string Days { get; set; }
    public System.DateTime Time { get; set; }
}

Если вы хотите исключить тип из модели, используйте атрибут NotMapped или API DbModelBuilder.Ignore fluent.

modelBuilder.Ignore<Department>();

Соглашение о первичном ключе

Сначала код определяет, что свойство является первичным ключом, если свойство класса называется "ID" (не учитывает регистр), или имя класса, за которым следует идентификатор. Если тип свойства первичного ключа является числовым или GUID, он будет настроен в качестве столбца идентификации.

public class Department
{
    // Primary key
    public int DepartmentID { get; set; }

    . . .  

}

Соглашение о связи

В Entity Framework свойства навигации предоставляют способ навигации между двумя типами сущностей. Каждый объект может иметь свойство навигации для каждой связи, в которой она участвует. Свойства навигации позволяют перемещаться по связям и управлять ими в обоих направлениях, возвращая объект-ссылку (если кратность равна одному или нулю) или коллекцию (если кратность равна многим). Code First определяет связи на основе навигационных свойств, заданных в ваших типах.

Помимо свойств навигации рекомендуется включить свойства внешнего ключа в типы, представляющие зависимые объекты. Любое свойство с тем же типом данных, что и свойство основного первичного ключа и имя, которое следует одному из следующих форматов, представляет внешний ключ для связи: "<имя свойства навигации><имя первичного ключа основного>", "<имя основного класса><имя первичного ключа" или ">имя основного свойства< первичного ключа". Если найдено несколько совпадений, приоритет присваивается в указанном выше порядке. Обнаружение внешнего ключа не зависит от чувствительности к регистру. При обнаружении свойства внешнего ключа код сначала определяет кратность связи на основе допустимости null внешнего ключа. Если свойство равно null, то связь регистрируется как необязательное; в противном случае связь регистрируется по мере необходимости.

Если внешний ключ для зависимой сущности не имеет значения NULL, код сначала задает каскадное удаление связи. Если внешний ключ зависимой сущности может принимать значение NULL, Code First не устанавливает каскадное удаление на отношении, и при удалении главной сущности внешний ключ будет установлен в NULL. Кратность и каскадное удаление, задаваемые по умолчанию, можно переопределить с помощью флюентного API.

В следующем примере свойства навигации и внешний ключ используются для определения связи между классами Department и Course.

public class Department
{
    // Primary key
    public int DepartmentID { get; set; }
    public string Name { get; set; }

    // Navigation property
    public virtual ICollection<Course> Courses { get; set; }
}

public class Course
{
    // Primary key
    public int CourseID { get; set; }

    public string Title { get; set; }
    public int Credits { get; set; }

    // Foreign key
    public int DepartmentID { get; set; }

    // Navigation properties
    public virtual Department Department { get; set; }
}

Замечание

Если у вас есть несколько связей между одинаковыми типами (например, предположим, что вы определяете класс Person и Book , где класс Person содержит свойства навигации ReviewedBooks и AuthoredBooks , а класс Book содержит свойства навигации "Автор и рецензент ") необходимо вручную настроить отношения с помощью заметок данных или свободного API. Для получения дополнительной информации см. разделы Аннотации данных — связи и Fluent API — связи.

Соглашение о сложных типах данных

Когда Code First обнаруживает определение класса, в котором первичный ключ не может быть выведен, и первичный ключ не регистрируется с помощью заметок данных или api fluent, то тип автоматически регистрируется в качестве сложного типа. Для обнаружения сложных типов также требуется, чтобы тип не имел свойств, которые ссылаются на типы сущностей, и чтобы на него не ссылались через свойство коллекции другого типа. Учитывая следующие определения классов Code First, будут выводить, что Details является сложным типом, так как он не имеет первичного ключа.

public partial class OnsiteCourse : Course
{
    public OnsiteCourse()
    {
        Details = new Details();
    }

    public Details Details { get; set; }
}

public class Details
{
    public System.DateTime Time { get; set; }
    public string Location { get; set; }
    public string Days { get; set; }
}

Соглашение о строке подключения

Чтобы узнать о соглашениях, которые DbContext использует для обнаружения подключения, см. статью "Подключения и модели".

Удаление соглашений

Вы можете удалить любое из соглашений, определенных в пространстве имен System.Data.Entity.ModelConfiguration.Conventions. В следующем примере удаляется PluralizingTableNameConvention.

public class SchoolEntities : DbContext
{
     . . .

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Configure Code First to ignore PluralizingTableName convention
        // If you keep this convention, the generated tables  
        // will have pluralized names.
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

Пользовательские соглашения

Пользовательские соглашения поддерживаются, начиная с EF6. Дополнительные сведения см. в пользовательских соглашениях Code First.