Идентификатор gets не определен visual studio
Я работаю над частью "драйвера" моего назначения программирования, и я продолжаю получать эту абсурдную ошибку:
ошибка C2065: 'cout': необъявленный идентификатор
Я даже пытался использовать std:: cout, но я получаю еще одну ошибку, которая говорит: IntelliSense: пространство имен "std" не имеет члена "cout" , когда у меня есть объявленный с использованием пространства имен std, включая iostream +, я даже пытался использовать ostream
Я знаю, что это стандартный вопрос о нобе, но это меня насторожило, и я новичок (это означает: я запрограммировал раньше. )
Я использую Visual Studio 2010 и запускаю Windows 7. Все файлы .h имеют "использование пространства имен std" и включают iostream и ostream.
ОТВЕТЫ
Ответ 1
Это не будет работать.
Ответ 2
напишите этот код, он отлично работает.
Ответ 3
Ответ 4
include "stdafx.h" в порядке
Но вы не можете использовать cout , если вы не включили using namespace std
Если вы не включили пространство имен std, вам нужно написать std::cout вместо простого cout
Ответ 5
Я видел, что если вы используете
тогда вы получите эту проблему.
Если вы используете
(уведомление - без .h)
то вы не получите проблему, о которой вы упомянули.
Ответ 6
Ответ 7
Нижеприведенный код компилируется и запускается правильно для меня, используя gcc. Попробуйте скопировать/вставить это и посмотреть, работает ли он.
Ответ 8
Если единственным файлом, который вы включаете, является iostream, и он все еще говорит undefined, то, возможно, iostream не содержит того, что он должен был. Возможно ли, что у вас есть пустой файл, совпадающий по имени "iostream" в вашем проекте?
Ответ 9
Я видел похожие вещи, когда я использовал расширение .c файла с кодом С++. Кроме этого, я должен согласиться со всеми о багги установке. Это работает, если вы попытаетесь скомпилировать проект с более ранней версией VS? Попробуйте VС++ Express 2008. Его бесплатно на msdn.
Ответ 10
Такое глупое решение в моем случае:
Выше было указано в качестве примера a, когда я изменил его, чтобы он был похож на пример b ниже.
Мой код составлен как шарм. Попробуйте, гарантированно сработает.
Ответ 11
прежде чем вы начнете эту программу, избавитесь от всего кода и сделайте простой мир привет внутри основного. Включать только iostream и использовать пространство имен std;. Постепенно добавьте его, чтобы найти свою проблему.
Ответ 12
У меня есть VS2010, Beta 1 и Beta 2 (один на моей рабочей машине и один на дому), и я использовал std множество без проблем. Попробуйте ввести:
И посмотрите, дает ли Intellisense что-нибудь. Если это дает вам обычный материал ( abort , abs , acos и т.д.), За исключением cout , ну тогда это довольно головоломка. Определенно посмотрите на ваши заголовки С++ в этом случае.
Помимо этого, я бы просто добавил, чтобы убедиться, что вы используете обычный пустой проект (не CLR, где Intellisense поврежден), и что вы на самом деле пытались построить проект хотя бы один раз. Как я уже упоминал в комментарии, VS2010 анализирует файлы после добавления include ; возможно, что что-то застряло в парсере, и он не сразу "нашел" cout . (В этом случае попробуйте перезапустить VS, возможно?)
Ответ 13
У меня была такая же проблема при запуске проекта ms С++ 2010 с нуля - я удалил все файлы заголовков, сгенерированные с помощью ms, но использовал:
Мне пришлось включить stdafx.h , поскольку это вызвало ошибку, в которой он не был.
Ответ 14
из вашего .cpp файла, создайте файл заголовка и поместите его в файл .h. Затем добавьте
в верхней части вашего .cpp-кода. Затем запустите его снова.
Ответ 15
Вы уверены, что он компилируется как С++? Проверьте имя файла (он должен заканчиваться на .cpp ). Проверьте настройки проекта.
Нет ничего плохого в вашей программе, а cout находится в namespace std . Ваша установка VS 2010 Beta 2 является дефектной, и я не думаю, что это просто ваша установка.
Я не думаю, что VS 2010 готов к С++. Стандартная программа "Hello, World" не работала на бета-версии 1. Я просто попытался создать тестовое консольное приложение Win32, а сгенерированный файл test.cpp не имел функции main() .
У меня действительно очень плохое чувство о VS 2010.
Ответ 16
Попробуй, это сработает. Я проверил его в Windows XP, Visual Studio 2010 Express.
Ответ 17
Ответ 18
В Visual Studio используйте весь ваш фильтр заголовка ниже "stdafx.h".
Ответ 19
Включите библиотеку std, вставив следующую строку вверху вашего кода:
Ответ 20
обычно сохраняется в папке C:\Program Files\Microsoft Visual Studio 8\VC\include. Сначала проверьте, все ли он там. Затем выберите "Инструменты + варианты", "Проекты и решения", "Каталоги VС++", выберите "Включить файлы" в поле "Показать каталоги для" и дважды проверьте, что включение (VCInstallDir) включено в список.
Ответ 21
Я столкнулся с этой ошибкой после того, как установил vs 2010 и просто пытался получить почти идентичную программу для работы.
Я уже делал кодировку ваниль C в коробках в стиле unix, решил, что немного поиграю с этим.
Первая программа, которую я пробовал, была:
Большая вещь, чтобы заметить здесь. если вы все сделали C-кодирование,
Выглядит странно. это должно быть:
В моем случае я просто изменил программу на:
И он отлично работал.
Примечание. Используйте CTRL + F5, чтобы окно консоли закрывалось, чтобы вы могли видеть результаты.
Ответ 22
Просто используйте printf !
Включите stdio.h в заголовочный файл stdafx.h для printf .
Ответ 23
Ответ 24
Это был компилятор - теперь я использую Eclipse Galileo, и программа работает как чудо
Компилятору не удалось разрешить ссылку на идентификатор даже при поиске с зависимостью от аргументов.
Remarks
Чтобы устранить эту ошибку, сравните использование идентификатора с объявлением идентификатора для Регистра и проверки орфографии. Убедитесь, что операторы разрешения области и пространство имен, использующие директивы , используются правильно. Если идентификатор объявлен в файле заголовка, убедитесь, что заголовок включен перед ссылкой на идентификатор. Если идентификатор должен быть видимым извне, убедитесь, что он объявлен в любом исходном файле, который его использует. Также убедитесь, что объявление идентификатора или определение не исключены директивами условной компиляции.
изменения в удалении устаревших функций из библиотеки времени выполнения C в Visual Studio 2015 могут вызвать C3861. Чтобы устранить эту ошибку, удалите ссылки на эти функции или замените их безопасными альтернативами, если они есть. Дополнительные сведения см. в разделе устаревшие функции.
если ошибка C3861 появляется после миграции проекта из предыдущих версий компилятора, могут возникнуть проблемы, связанные с поддерживаемыми версиями Windows. Visual C++ больше не поддерживает создание программ для Windows 95, Windows 98, Windows ME, Windows NT и Windows 2000. Если ваши макросы WINVER или _WIN32_WINNT предназначены для одной из этих версий Windows, необходимо изменить такие макросы. Дополнительные сведения см. в разделе изменение winver и _WIN32_WINNT.
Примеры
Неопределенный идентификатор
Следующий пример приводит к возникновению ошибки C3861, поскольку идентификатор не определен.
Идентификатор не находится в области
Следующий пример приводит к возникновению ошибки C3861, поскольку идентификатор виден только в области файла его определения, если только он не объявлен в других исходных файлах, которые его используют.
Требуется квалификация пространства имен
Для классов исключений в стандартной библиотеке C++ требуется std пространство имен.
Вызвана устаревшая функция
Устаревшие функции были удалены из библиотеки CRT.
Функции ADL и Friend
Следующий пример приводит к возникновению ошибки C3767, так как компилятор не может использовать поиск с зависимостью от аргумента для FriendFunc :
Чтобы устранить эту ошибку, объявите дружественную команду в области класса и определите ее в области видимости пространства имен:
Пример текстов ошибок:
- Для компилятора Visual Studio: error C2065: 'cout' : undeclared identifier
- Для компилятора GCC: 'cout' undeclared (first use in this function)
Решение
Чаще всего они приходят из-за того, что забывают включить заголовочный файл, содержащий объявление функции, например, эта программа выдаст ошибку «необъявленный идентификатор»:
Отсутствует заголовок
Если вы написали заголовок и включили его правильно, заголовок может содержать неправильный включить охрану .
Переменная с ошибкой
Другой распространенный источник ошибки новичка возникает, когда вы неправильно написали переменную:
Неправильный объем
Например, этот код выдаст ошибку, потому что вам нужно использовать std::string :
Использовать до объявления
Или добавить декларацию g до f :
stdafx.h не сверху (специфично для VS)
Не стесняйтесь редактировать этот ответ.
Другие решения
Рассмотрим похожую ситуацию в разговоре. Представьте, что ваш друг говорит вам: «Боб идет на ужин», а ты не представляешь, кто такой Боб. Вы будете в замешательстве, верно? Твой друг должен был сказать: «У меня есть коллега по работе по имени Боб. Боб подходит к обеду». Теперь Боб объявлен, и вы знаете, о ком говорит ваш друг.
Компилятор выдает ошибку «необъявленный идентификатор», когда вы пытаетесь использовать какой-то идентификатор (который будет именем функции, переменной, класса и т. Д.), И компилятор не видит объявления для него. То есть компилятор понятия не имеет, о чем вы говорите, потому что раньше его не видел.
Если вы получаете такую ошибку в C или C ++, это означает, что вы не сказали компилятору о том, что вы пытаетесь использовать. Объявления часто встречаются в заголовочных файлах, поэтому, скорее всего, это означает, что вы не включили соответствующий заголовок. Конечно, может случиться так, что вы просто не помните, чтобы объявить сущность вообще.
или используя класс, как
В C и C ++ все имена должны быть объявлены перед использованием. Если вы попытаетесь использовать имя переменной или функции, которая не была объявлена, вы получите ошибку «необъявленный идентификатор».
Вы исправляете ошибки такого рода, проверяя, что функции и переменные объявлены до их использования. В случае printf вам нужно включить заголовочный файл <stdio.h> (или же <cstdio> в C ++).
Для стандартных функций я рекомендую вам проверить, например, этот справочный сайт , и найдите функции, которые вы хотите использовать. Документация для каждой функции говорит вам, какой заголовочный файл вам нужен.
означает, что вы используете имя printf но компилятор не видит, где было объявлено имя, и, соответственно, не знает, что это значит.
Любое имя, используемое в программе, должно быть объявлено до ее использования. Компилятор должен знать, что обозначает имя.
В этом конкретном случае компилятор не видит объявление имени printf , Как мы знаем (но не компилятор) это имя стандартной функции C, объявленной в заголовке <stdio.h> в C или в заголовке <cstdio> в C ++ и размещены в стандарте ( std:: ) и глобальный ( :: ) (не обязательно) пространства имен.
Поэтому, прежде чем использовать эту функцию, мы должны предоставить объявление ее имени компилятору путем включения соответствующих заголовков.
Например
C:
C ++:
Иногда причиной такой ошибки является простая опечатка. Например, давайте предположим, что вы определили функцию PrintHello
но в основном вы сделали опечатку и вместо PrintHello ты напечатал printHello с строчной буквы «р».
В этом случае компилятор выдаст такую ошибку, потому что он не видит объявление имени printHello , PrintHello а также printHello два разных имени, одно из которых было объявлено, а другое не объявлено, но используется в теле основного
Другая возможная ситуация: доступ к родительскому элементу (классу шаблона) в классе шаблона.
Это похоже на использование функции без ее объявления. заголовочный файл будет содержать
функция printf (). Включите заголовочный файл в вашу программу, это решение для этого.
Некоторые пользовательские функции могут также вызывать ошибки, если они не были объявлены перед использованием. Если
это используется во всем мире без проб.
В большинстве случаев, если вы уверены, что импортировали данную библиотеку, Visual Studio поможет вам с IntelliSense.
Функции gets() и puts() позволяют читать и выводить строки на консоль.
Функция gets() читает строку символов, введенных с клавиатуры и помещает их по адресу, указанному в аргументе. Можно набирать символы, пока не будет нажат ввод. Символ, соответствующий клавише ввод - возврат каретки, - не станет частью строки. Вместо этого в конце строки появится нулевой символ, и gets() закончит работу. Фактически невозможно использовать gets() для получения возврата каретки (можно использовать getchar() и ее варианты). Если при вводе допущены ошибки, то они могут быть исправлены нажатием на клавишу BACKSPASE перед нажатием ввода. Функция gets() имеет прототип:
где str - это массив символов. Функция gets() возвращает указатель на str. Следующая программа осуществляет чтение строки в массив str и выводит ее длину:
Имеется проблема, связанная с gets(), о которой следует знать: используя gets(), можно перейти границы массива, с которым она вызывалась. Это возможно, поскольку не существует способа указать gets(), где находится граница массива. Например, если вызвать gets() с массивом длиной в 40 байт, а затем ввести 40 или более символов, то произойдет выход за пределы массива. Это, естественно, вызывает проблемы и часто может привести к краху системы. В качестве альтернативы можно использовать функцию fgets() (описываемую далее), которая позволяет указать максимальную длину. Единственная проблема, связанная с fgets(), заключается в том, что она сохраняет символ новой строки. Если этот символ не нужен, то следует удалить его вручную. Возможно, в будущем будет осуществлена замена gets(), но на сегодняшний день данная функция предоставляет самый легкий способ чтения строки с клавиатуры, надо просто быть внимательным.
Функция puts() выводит передаваемый ей аргумент на экран, завершая вывод переходом на новую строку. Она имеет следующий прототип:
int puts(const char *str);
Здесь str - это выводимая строка. Функция возвращает нецелое число в случае удачи и EOF - в случае неудачи. Она понимает коды с обратным слэшем, как и printf(), например \t воспринимается как табуляция. Вызов функции puts() требует гораздо меньше процессорного времени на реализацию, чем printf(), поскольку puts() выводит только строку символов. Она не может выводить числа или выполнять преобразование форматов. Она занимает, меньше места и работает быстрее printf(). Следующий оператор выводит «hello» на экран:
Простейшие функции, выполняющие операции консольного ввода/вывода, приведены в таблице.
В предыдущей статье вы узнали о соглашениях Code-First по конфигурации столбцов таблицы базы данных. Здесь мы рассмотрим соглашения по созданию между таблицами в базе данных. Эти соглашения определяют то, как связываются классы в модели и настраиваются внешние ключи, определяющие эти связи. Для настройки этих конфигураций в основном используется Fluent API, а не аннотации данных.
Вы уже видели ранее использование некоторых связей между таблицами. Например, при рассмотрении примера в статье “Использование Code-First” мы создали следующую модель данных:
Code-First видит в этом примере, что вы определили навигационное свойство Orders в таблице Customer, ссылающееся на коллекцию объектов Order, что говорит о создании отношения один-ко-многим (one-to-many) между этими таблицами. Так же Code-First определит автоматически эту связь, создав внешний ключ для таблицы Order, привязанный к первичному ключу CustomerId таблицы Customer.
Далее мы опишем все соглашения, которые используются в Code-First для описания связей между таблицами.
Использование навигационных свойств
Как вы видели, Code-First автоматически добавляет отношения между таблицами если видит навигационные свойства в коде модели и, необязательно, явно указанные внешние ключи. Мы подробно опишем использование внешних ключей чуть позже, а сейчас давайте остановимся на определении отношений между таблицами без внешних ключей.
Отношения между таблицами можно определить в модели только за счет использования навигационных свойств, которые могут быть указаны в обеих таблицах (двусторонняя связь, как показано в примере выше) или в одной таблице (односторонняя связь). Ниже описаны некоторые соглашения Code-First при использовании навигационных свойств:
Code-First также будет предполагать связь один-ко-многим, если навигационное свойство используется только в одной таблице, вне зависимости от типа этого свойства (т.е. если используется односторонняя связь).
Если в обеих таблицах навигационные свойства имеют тип коллекций, то Code-First предполагает наличие связи между ними многие-ко-многим (many-to-many).
Если в обеих таблицах навигационные свойства представлены в виде ссылок друг на друга, то Code-First предполагает отношение между таблицами один-к-одному (one-to-one).
В случае реализации отношения один-к-одному, вы должны будете предоставить некоторую дополнительную информацию, чтобы Code-First знал, какая сущность является основной, а какая зависимой. Если в таблицах явно не указан внешний ключ, то Code-First смоделирует отношение один-или-ноль-к-одному (zero-or-one-to-one, 0..1-1), т.е. добавление данных в главную таблицу, необязательно должно вести к добавлению данных в зависимую таблицу.
Посмотрев пример нашей модели, показанной выше, можно проследить использование этих соглашений на классах Customer и Order. Например, Code-First автоматически создаст отношение между таблицами один-ко-многим, т.к. мы использовали тип коллекции в одной таблице и простую ссылку в другой. Также можно догадаться, что в отношении этих таблиц Customer будет главной, а Order зависимой, т.е. мы можем вставить данные заказчика в таблицу Customer, не добавляя при этом для него заказы. И наоборот, в таблицу Order мы можем вставить только заказ, привязанный к конкретному покупателю.
Стоит также сказать, что без явного указания внешних ключей в коде модели, Code-First будет генерировать эти ключи автоматически, используя имя, сочетающие в себе название базовой таблицы и первичного ключа этой таблицы. Например, для таблицы Order будет сгенерирован внешний ключ Customer_CustomerId.
Большую часть конфигурации по настройке навигационных свойств мы можем выполнить с помощью Fluent API. Некоторые настройки можно выполнить и с помощью аннотаций данных, хотя в данном случае этот подход намного более ограничен, чем использование Fluent API. Например, мы можем указать для автоматически генерируемого внешнего ключа, чтобы он не поддерживал значения NULL:
Если вы запустите приложение и обновите структуру базы данных, то обнаружите, что Code-First изменил тип внешнего ключа Customer_CustomerId – теперь он не может поддерживать значения NULL:
Напомню, что мы используем в качестве примера приложение, созданное в статье “Использование Code-First” и для воссоздания базы данных при изменении модели требуется либо ее ручное удаление всякий раз, когда база данных изменилась, либо использование настроек Code-First по автоматическому обнаружению изменений в модели.
Настройка отношений с помощью Fluent API может показаться несколько запутанной, если вы не потратите некоторое время, чтобы понять основные идеи. При использовании аннотаций данных для настройки отношений вы просто устанавливаете атрибуты для навигационных свойств в коде модели. Это сильно отличается от подхода с Fluent API, где вы должны в буквальном смысле настроить отношения между таблицами. Для этого используется следующий общий шаблон (он не зависит от того, хотите ли вы использовать одностороннюю или двустороннюю связь):
Параметр Multiplicity в этом шаблоне указывает на окончание используемых методов Has… и With…, он может иметь следующие значения: Optional (навигационное свойство может иметь один или ноль экземпляров), Required (навигационное свойство может иметь только один экземпляр) и Many (навигационное свойство содержит коллекцию экземпляров).
Соответственно Entity Framework определяет следующий набор методов, определяющих настройки первичных навигационных свойств:
Для настройки отношений во второй таблице, используются следующие методы:
При вызове этих методов в качестве параметра, им передается делегат, в котором указывается навигационное свойство. При использовании односторонней связи (когда в одной из таблиц отсутствует навигационное свойство), можно вызвать соответствующий метод без параметров. Ниже показан пример настройки навигационных свойств для наших таблиц, который соответствует автоматическим соглашениям Code-First. Т.е. фактически он создает связь один-ко-многим между нашими таблицами, где таблица Customer является главной:
Ранее мы показали, как с помощью аннотаций данных можно ограничить поддержку NULL значений для автоматически сгенерированного внешнего ключа. Давайте реализуем это с помощью Fluent API:
В этом примере мы просто поменяли вызов метода WithOptional() на WithRequired(). Если вы запустите пример и посмотрите на структуру таблицы Orders, то увидите, что ее структура аналогична той, которая показана на первом рисунке в статье, когда мы использовали атрибуты метаданных. На рисунке ниже наглядно показано, как выполняется этот запрос:
Указание внешних ключей
Ранее мы рассмотрели, как реализовать отношения между таблицами без прямого использования внешних ключей. Например, класс Order содержит свойство-ссылку на класс Customer, но при этом в этом классе не определено свойство, которое будет использоваться в качестве внешнего ключа для связи между таблицами. В этом случае мы видели, что Code-First автоматически сгенерирует внешний ключ за вас. Теперь давайте рассмотрим, что происходит, если мы явно задаем внешний ключ.
Первое что мы сделаем, это добавим новое свойство CustomerId в класс модели Order:
Запустите приложение. Code-First поймет, что вы внесли изменения в модель и воссоздаст базу данных. Если вы рассмотрите структуру столбцов таблицы Order, то заметите, что Code-First автоматически распознал поле CustomerId как внешний ключ и заменил автоматически генерируемый ключ Customer_CustomerId на CustomerId:
Как вы понимаете, Code-First использует определенные соглашения при поиске внешнего ключа в свойствах модели. Эти правила основаны на имени свойства и придерживаются следующих шаблонов:
В нашем случае имя свойства CustomerId подходит под первое правило, т.к. в главной таблице Customer используется одноименное свойство, которое является первичным ключом таблицы. Также стоит отметить, что Code-First не чувствителен к регистру символов при поиске внешнего ключа, т.е. если бы в таблице Order у нас было бы свойство CusToMeRId, то Code-First автоматически бы распознал его как внешний ключ.
К данному моменту может возникнуть вопрос, зачем может понадобиться явное определение внешних ключей в классе модели, если Code-First способен автоматически создавать эти ключи? Ответом на этот вопрос будет то, что иногда гораздо удобней получить доступ к родительскому объекту в коде через внешний ключ, нежели чем через ссылку. Например, в коде вы могли бы создать новый объект Order и указать через ссылку объект Customer, к которому он должен принадлежать:
Очевидно, для того, чтобы загрузить объект Customer в экземпляр myCustomer, вам необходимо будет сначала обратиться к базе данных. Использование внешнего ключа позволяет просто указать идентификатор заказчика не ссылаясь на него. Чтобы получить идентификатор заказчика, зачастую нужно также обратиться к базе данных, но бывают случаи, когда у вас есть доступ к значению ключа этого объекта. Например, если мы знаем что заказ myOrder принадлежит заказчику с идентификатором 5, мы могли бы использовать внешний ключ вместо ссылки на объект:
Кроме того, при использовании ссылки иногда возникает более серьезная ошибка. Entity Framework отслеживает состояние объектов сущностных классов и при их изменении помечает объект, изменяя свойство DbEntityEntry.State. Если вы создадите новый объект myOrder, укажите в нем ссылку на уже существующий в памяти объект myCustomer и попытаетесь сохранить объект myCustomer в базе данных, то EF пометит состояние этого объекта как Added, а не Modified, т.к. в коде изменился список заказов, связанных с этим покупателем и EF предполагает, что был создан новый покупатель. В результате в таблицу будет добавлен новый заказчик, хотя предполагалось просто добавить заказ для уже существующего заказчика. Эту проблему можно избежать либо сохранив в базе данных только объект myOrder, либо используя внешний ключ.
Есть еще один момент, который нужно упомянуть при обсуждении соглашений Code-First по внешним ключам. Когда ранее мы не использовали внешних ключей, Code-First автоматически генерировал ключ, который поддерживал значения NULL. Если вы взгляните на рисунок выше, то увидите, что внешний ключ CustomerId в таблице Order имеет не обнуляемый тип NOT NULL, поэтому вы не сможете сохранить новый заказ не указав идентификатор покупателя (используя внешний ключ или ссылку). Тем не менее, можно явно указать, что внешний ключ должен поддерживать значения NULL. Для этого измените тип свойства CustomerId в классе модели Order на обнуляемый тип данных:
Итак, из всего сказанного выше, можно сделать вывод, что Code First позволяет определять отношения без использования внешних ключей в ваших классах. Тем не менее, разработчики иногда сталкиваются с некоторыми запутанными ошибками, когда работают с моделями, не имеющими внешних ключей, как было показано ранее.
Настройка внешних ключей в обход соглашениям Code-First
Иногда может возникнуть вопрос, что происходит, если имя вашего внешнего ключа не соответствует соглашениям Code-First? Например, вы могли бы использовать в таблице Order внешний ключ с именем UserId, как показано ниже:
Если вы запустите приложение и воссоздадите базу данных, то увидите, что Code-First проигнорировал поле UserId и создал автоматически генерируемый внешний ключ Customer_CustomerId, а поле UserId было добавлено как обычный столбец. Вы можете решить эту проблему используя атрибут ForeignKey в классе модели данных, как показано в примере:
В конструкторе этого атрибута указывается имя навигационного свойства, если оно имеется в классе модели. Альтернативным способом является применение атрибута ForeignKey к навигационному свойству:
В данном случае в конструкторе указывается имя свойства, являющегося внешним ключом. В Fluent API используется специальный метод HasForeignKey(), как показано в примере ниже:
Работа с обратными навигационными свойствами
Пока мы использовали по одному навигационному свойству между двумя классами модели, Code-First понимал, как настроить отношения между ними. Существует такие случаи, когда между двумя таблицами базы данных нужно определить несколько отношений. Например, таблица Customers могла бы ссылаться на все заказы, на обработанные заказы (которые оплатил покупатель) и необработанные заказы. Логичнее всего решить данную проблему, это просто добавить новый столбец, например IsProcess, в таблицу Orders, который имел бы логическое значение и указывал бы на то, обработан заказ или нет. Но также эту проблему можно решить использовав три внешних ключа, связывающих эти таблицы.
В модели классов это решение будет выглядеть следующим образом:
В данном примере Code-First не сможет автоматически распознать связь между навигационными свойствами этих классов. Если вы выполните этот пример, то увидите, что в созданной таблице Orders было добавлено пять внешних ключей – по одному для каждого несвязанного навигационного свойства, и один ключ для связанных свойств Orders и Customer (если вы удалите настройку Fluent API, показанную ранее, в которой мы привязали эти свойства и указали внешний ключ, то Code-First сгенерирует 6 внешних ключей).
Согласно соглашениям, Code-First может самостоятельно определить двунаправленную связь между таблицами, только когда существует всего одна пара навигационных свойств. В нашем примере их несколько, поэтому Code-First создаст по одному внешнему ключу для каждого навигационного свойства.
Теперь будет создано три внешних ключа, как и требовалось:
Использование однонаправленной связи между таблицами
В предыдущих примерах мы рассмотрели способы создания двунаправленных связей между таблицами, т.е. когда в обоих классах модели определяется навигационное свойство. Тем не менее определение пары навигационных свойств не является обязательным при работе с Entity Framework. В нашей модели мы можем указать ссылку только в одном классе, например, в классе Customer оставить ссылку на коллекцию объектов Order, а в классе Order удалить ссылку на Customer:
В этом примере мы не стали удалять внешний ключ CustomerId, благодаря чему, Entity Framework четко определяет связь между таблицами с использованием этого ключа, используя соглашения, описанные выше. Теперь давайте сделаем еще один шаг и удалим внешний ключ из таблицы Order:
Ранее мы уже сказали, что по соглашению Code-First сгенерирует автоматически внешний ключ, если он не объявлен явно в классе модели. Это же соглашение работает, если мы используем однонаправленную связь между таблицами. Класс Customer по прежнему имеет навигационное свойство, определяющие его отношение с Order, поэтому будет генерироваться внешний ключ с именем Customer_CustomerId в таблице Orders.
Что будет если мы захотим удалить оба навигационных свойства, а использовать для связи явно заданный внешний ключ? Сама платформа Entity Framework поддерживает этот сценарий, но не подход Code-First. В Code-First требуется для создания отношений определить как минимум одно навигационное свойство, иначе свойство модели, которое мы планировали использовать как внешний ключ, будет просто преобразовано в столбец в таблице и отношения между таблицами не будут созданы.
Теперь давайте рассмотрим случай, когда мы явно указываем внешний ключ в зависимой таблице и при этом желаем изменить его имя, которое не будет соответствовать соглашениям Code-First по именованию внешних ключей, например:
Как мы описывали раньше, чтобы явно указать классу Order, что UserId является внешним ключом, можно использовать атрибут ForeignKey и передать ему имя навигационного свойства в параметре. Что делать, если мы используем одностороннюю связь и в классе Order не используем навигационное свойство?
Для решения этой проблемы мы можем использовать этот атрибут в главной таблице к навигационному свойству Orders или использовать Fluent API, как показано в примере ниже:
Обратите внимание, что при использовании Fluent API в вызове метода WithRequired() мы не передаем параметр делегата с выбором навигационного свойства из модели, т.к. используем одностороннюю связь и у нас отсутствует навигационное свойство в классе Order.
Читайте также: