powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / EF Code First - не внести данные в связанную таблицу
25 сообщений из 25, страница 1 из 1
EF Code First - не внести данные в связанную таблицу
    #39335976
ValGer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый день!
Разбираюсь с EF (раньше не работал) и выполняю примеры. Везде стандартным является пример "Клиент и его Заказы". Промеры везде доводятся до внесения информации в таблицу Customers и на этом всё заканчивается. Я решил пойти дальше и занести хотя бы один заказ, который делает клиент и ... ничего не получилось. Выдаётся ошибка. Что я не так делаю?
Вот примитивная программа:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
using System;
using System.Linq;
using System.Data.Entity;

namespace EF_CodeFirst1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new ShopDbContext())
            {
                var customer = new Customer();
                customer.Name = "Круглова Анна";
                db.Customers.Add(customer);
                db.SaveChanges();

                Customer cust = db.Customers.Where(tCust => tCust.CustomerId == 1)
                                            .SingleOrDefault<Customer>();

                Console.WriteLine("  Код клиента = {0} Фамилия = {1}",
                                    cust.CustomerId, cust.Name);    // Всё ОК!

                // Пытаюсь занести заказ для клиента, который уже присутствует в таблице Customers
                var order = new Order();
                order.ProductName = "Хлеб";
                order.Customer.CustomerId = 1;
                db.Orders.Add(order);
                db.SaveChanges();
            };

            Console.WriteLine("  Для завершения программы нажмите на любую клавишу");
            Console.ReadKey();
        }
    }

    public class Customer
    {
        public int      CustomerId  { get; set; }
        public string   Name        { get; set; }
    }

    public class Order
    {
        public int OrderId { get; set; }
        public string ProductName { get; set; }

        // Ссылка на покупателя - как в учебниках!
        public Customer Customer { get; set; }
    }

    public class ShopDbContext : DbContext
    {
        public ShopDbContext()
            : base("MyProductShop") { }

        public DbSet<Customer>  Customers   { get; set; }
        public DbSet<Order>     Orders      { get; set; }
    }

}



Ничего хитрого - всё как в учебниках!
При выполнении первого фрагмента с Customers база прекрасно создаётся (SQLEXRESS), клиент вносится, читается, однако задать ему заказ не получается - см. рисунок.
В чём проблема?
С уважением ВВГ
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39335996
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ValGer
Код: c#
1.
 public virtual Customer Customer { get; set; }



Ты забыл добавить virtual. Если в учебниках этого нет, то это плохие учебники.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336042
ValGer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt,

В учебнике (professorWeb.ru) этого действительно нет, но я внёс по вашей рекомендации и ничего не изменилось. Та же самая ошибка (см. рисунок выше)


Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
   public class Order
    {
        public int OrderId { get; set; }
        public string ProductName { get; set; }

        // Ссылка на покупателя
        public virtual Customer Customer { get; set; }
    }
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336066
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ValGer
Код: c#
1.
2.
3.
4.
5.
var order = new Order();
                order.ProductName = "Хлеб";
                order.Customer.CustomerId = 1;
                db.Orders.Add(order);
                db.SaveChanges();



Добавить virtual -- это правильно и будет работать для объектов извлечённых из БД.

Но тут сам смотри что происходит. Ты создал новый Order, а ссылку на Customer не присвоил. Надо так:

ValGer
Код: c#
1.
2.
3.
4.
5.
6.
var order = new Order();
                order.ProductName = "Хлеб";
                // order.Customer.CustomerId = 1; -- НЕ ПРАВИЛЬНО!
                order.Customer = customer; // ПРАВИЛЬНО
                db.Orders.Add(order);
                db.SaveChanges();
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336093
ValGer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt,

Помогло, спасибо большое!
Может смысл virtual поясните - пока не очень понимаю для чего он тут нужен!
С уважением ВВГ
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336133
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt
Ты забыл добавить virtual. Если в учебниках этого нет, то это плохие учебники.
А не ошибка ли дизайна EF использование virtual? Не правильнее ли было использовать атрибут?
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336137
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ValGerhVostt,

Помогло, спасибо большое!
Может смысл virtual поясните - пока не очень понимаю для чего он тут нужен!
С уважением ВВГ

Когда вы извлекаете объекты из БД вот так:


ValGer
Код: c#
1.
2.
Customer cust = db.Customers.Where(tCust => tCust.CustomerId == 1)
                                            .SingleOrDefault<Customer>();



То навигационные свойства, помеченные как virtual будут доступны. А без virtual они всегда будут null. Это называется ленивое извлечение данных.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336138
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЕвгенийВhVosttТы забыл добавить virtual. Если в учебниках этого нет, то это плохие учебники.
А не ошибка ли дизайна EF использование virtual? Не правильнее ли было использовать атрибут?

Не ошибка дизайна, а необходимость. Без virtual ты никак не сделаешь ленивое извлечение данных, никакие атрибуты тебе не помогут. Но если не добавлять virtual, данные по навигационным полям можно извлекать только с помощью жадной загрузки.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336151
E5
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
E5
Гость
Уважаемый hVostt , прошу Вас помочь с решением похожей проблемы. Ссылка . Спасибо
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336177
Фотография ЕвгенийВ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt
Не ошибка дизайна, а необходимость. Без virtual ты никак не сделаешь ленивое извлечение данных, никакие атрибуты тебе не помогут. Но если не добавлять virtual, данные по навигационным полям можно извлекать только с помощью жадной загрузки.
Типа делает наследника в котором переопределяет свойство с использованием отложенной загрузки?
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336200
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЕвгенийВhVosttНе ошибка дизайна, а необходимость. Без virtual ты никак не сделаешь ленивое извлечение данных, никакие атрибуты тебе не помогут. Но если не добавлять virtual, данные по навигационным полям можно извлекать только с помощью жадной загрузки.
Типа делает наследника в котором переопределяет свойство с использованием отложенной загрузки?

Ага. Это называется прокси-объект.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336827
fsharp_fsharp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttЕвгенийВпропущено...

Типа делает наследника в котором переопределяет свойство с использованием отложенной загрузки?

Ага. Это называется прокси-объект.

вроде в NH обязательность virtual убрали
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39336828
fsharp_fsharp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЕвгенийВhVosttТы забыл добавить virtual. Если в учебниках этого нет, то это плохие учебники.
А не ошибка ли дизайна EF использование virtual? Не правильнее ли было использовать атрибут?

конечно ошибка
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39337184
ValGer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fsharp_fsharphVosttпропущено...


Ага. Это называется прокси-объект.

вроде в NH обязательность virtual убрали

Что такое NH?
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39337209
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fsharp_fsharpвроде в NH обязательность virtual убрали

В EF virtual тоже не обязателен. Изначально. При чём очень-очень многие «понаехавшие» в разработку на EF из NH, проставляют virtual всем полям подряд, даже не навигационным. Вот это и есть глобальная ошибка дизайна NH, погубившая многие умы.


fsharp_fsharpконечно ошибка

Ошибка фигачить virtual на все поля. Ошибка не понимать, для чего он нужен. А так, это языковое средство, которое правильно используется, при правильном понимании.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39337213
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ValGerЧто такое NH?

NHibernate. Поделие с очевидным концом. В своё время был мастхев, но после выхода EF 4 и дальнейшим его развитием, стал абсолютно не нужен.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39337239
ValGer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttValGerЧто такое NH?

NHibernate. Поделие с очевидным концом. В своё время был мастхев, но после выхода EF 4 и дальнейшим его развитием, стал абсолютно не нужен.

Ага, понятно, спасибо!

Тут наткнулся в MSDN на пример и там для связанной таблицы (Order) поле связи определяется как обычным образом, так и с virtual-описателем. При этом после генерации таблицы получаем стандартное поле для связи (см. рисунок)
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
    public class Order
    {
        public int OrderId { get; set; }
        public string ProductName { get; set; }

        // Ссылка на покупателя
        public int CustomerId { get; set; }
        public virtual Customer Customer { get; set; }
    }



То есть, определений в классе 4, а полей в результирующей таблице 3 - virtual исчезает, но оставляет след в виде FK.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39337273
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ValGerПри этом после генерации таблицы получаем стандартное поле для связи (см. рисунок)

На самом деле это поле не обязательно. Достаточно одного навигационного, а поле EF создаст сам. Правда в таком случае нельзя будет задавать связь с помощью ID, необходимо будет найти запись предварительно и сохранить её в навигационном поле. Это более надёжно, но менее удобно.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39337361
ValGer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt,

Заметил ещё одну вещь - при генерации таблиц строковые поля таблиц получают свойство "Допускает значение NULL = True", а числовые и дата, как правило, имеют установку "... NULL = False". Если такие установки по умолчанию нас не устраивают, то как их можно поменять?
Для NULL = True, наверно можно пометить свойство класса в коде программы аннотацией [Required], а как сделать для NULL = False?
Я попробовал после генерации поменять этот атрибут для поля прямо в таблице на сервере - всё прошло потом нормально, без "ругани" от EF, что нужно делать миграцию и т.д. и т.п.
И, вообще, где можно найти достаточно полный список аннотаций?
С уважением ВВГ
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39339178
Gluck_13
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt
В EF virtual тоже не обязателен. Изначально. При чём очень-очень многие «понаехавшие» в разработку на EF из NH, проставляют virtual всем полям подряд, даже не навигационным . Вот это и есть глобальная ошибка дизайна NH, погубившая многие умы.



Если у тебя хоть одно замэпленное свойство сущности (даже не навигационное) не виртуально - то прокся не будет реализовывать для нее IEntityWithChangeTracker и IEntityWithRelationships (см. код System.Data.Entity.Core.Objects.Internal.IPocoImplementor.CheckType()). Это чревато тормозами при работе с большими объектными графами в ObjectContext.

Кстати, при внимательном чтении кода этого метода можно заметить еще одну тонкость, на которую мы однажды напоролись. В POCO свойства- коллекции должны быть объявлены именно как ICollection<T>. Одно время у нас коллекции были объявлены как IList<T>, в результате прокси для таких сущностей также не имплементировали IEntityWithChangeTracker и IEntityWithRelationships.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39339556
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gluck_13Если у тебя хоть одно замэпленное свойство сущности (даже не навигационное) не виртуально - то прокся не будет реализовывать для нее IEntityWithChangeTracker и IEntityWithRelationships (см. код System.Data.Entity.Core.Objects.Internal.IPocoImplementor.CheckType()). Это чревато тормозами при работе с большими объектными графами в ObjectContext.

Да ладно, гоняли какие-нибудь тесты? Сравнивали? Какие результаты?


Gluck_13В POCO свойства- коллекции должны быть объявлены именно как ICollection<T>.

Да, нужен именно ICollection<T>.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39339558
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ValGerЗаметил ещё одну вещь - при генерации таблиц строковые поля таблиц получают свойство "Допускает значение NULL = True", а числовые и дата, как правило, имеют установку "... NULL = False". Если такие установки по умолчанию нас не устраивают, то как их можно поменять?

Для строк NOT NULL достигается с помощью атрибута [Required], или в конфигурации отображения.

Для ValueType (дата/время, числа, булев) NULL достигается с помощью Nullable<T>, или модификатора «?»:

Код: c#
1.
public DateTime? SomeDate {get;set;]
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39339560
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ValGerИ, вообще, где можно найти достаточно полный список аннотаций?

Я не видел места, где были бы собраны все аннотации, в документации смотреть надо. Лучше минимизировать использование атрибутов и настраивать с помощью конфигураций и конвенций.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39339699
Gluck_13
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVosttGluck_13Если у тебя хоть одно замэпленное свойство сущности (даже не навигационное) не виртуально - то прокся не будет реализовывать для нее IEntityWithChangeTracker и IEntityWithRelationships (см. код System.Data.Entity.Core.Objects.Internal.IPocoImplementor.CheckType()). Это чревато тормозами при работе с большими объектными графами в ObjectContext.

Да ладно, гоняли какие-нибудь тесты? Сравнивали? Какие результаты?


Без реализации этих интерфейсов, например, убийственно долго идет анализ изменений DetectChanges, при наличии в контексте нескольких тысяч сущностей (понятно, такого надо избегать, но иногда очень надо :)). Ситуация усугубляется с увеличением количества навигационных связей в модели.

См. код в
ObjectStateManager.GetEntityEntriesForDetectChanges(...)
EntityEntry.SetChangeTrackingFlags()

Сущности, реализующие IEntityWithChangeTracker и IEntityWithRelationships, исключаются из процесса DetectChanges(), так как прокси автоматически обрабатывает все изменения, происходящие в ней, с поддержанием актуальности реляционных связей и трекинга изменений. Если код в GetEntityEntriesForDetectChanges обнаруживает, что в контекст загружены только "правильные" прокси, то DetectChanges просто ничего не делает.

Также существенно быстрее работает загрузка сущностей в контекст в режиме AppendOnly, производительность становится сопоставимой с режимом NoTracking.

Когда мы привели доменную модель (около 1500 классов с большим количеством реляционных связей) в соответствие с требованиями имплементора POCO-Proxy, производительность отдельных сценариев выросла в 10 и более раз (проверялось профайлером). После этого в Сontinuous Integration были добавлены тесты, проверяющие доменную модель на соответствие этим требованиям. Проверяется, что типы не должны быть sealed, для всех свойств через Reflection вызывается метод System.Data.Entity.Core.Objects.Internal.EntityProxyFactory.CanProxySetter(), для коллекций также проверяется, что они объявлены как ICollection<T>.

hVosttGluck_13В POCO свойства- коллекции должны быть объявлены именно как ICollection<T>.

Да, нужен именно ICollection<T>.

Тут был наш продолб, хочу просто чтоб другие не наступали на те же грабли. С другими объявлениями коллекций (например, IList<T>), на первый взгляд, вроде все работает, а потом начинают вылезать глюки, типа когда в контексте присваиваешь новому элементу из дочерней коллекции ссылку на головную сущность, она автоматически не добавляется в коллекцию головы.
...
Рейтинг: 0 / 0
EF Code First - не внести данные в связанную таблицу
    #39339869
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gluck_13Без реализации этих интерфейсов, например, убийственно долго идет анализ изменений DetectChanges, при наличии в контексте нескольких тысяч сущностей (понятно, такого надо избегать, но иногда очень надо :)). Ситуация усугубляется с увеличением количества навигационных связей в модели.

Согласен.

Gluck_13Также существенно быстрее работает загрузка сущностей в контекст в режиме AppendOnly, производительность становится сопоставимой с режимом NoTracking.

Хм.

Gluck_13Когда мы привели доменную модель (около 1500 классов с большим количеством реляционных связей) в соответствие с требованиями имплементора POCO-Proxy, производительность отдельных сценариев выросла в 10 и более раз (проверялось профайлером). После этого в Сontinuous Integration были добавлены тесты, проверяющие доменную модель на соответствие этим требованиям. Проверяется, что типы не должны быть sealed, для всех свойств через Reflection вызывается метод System.Data.Entity.Core.Objects.Internal.EntityProxyFactory.CanProxySetter(), для коллекций также проверяется, что они объявлены как ICollection<T>.

Мы избегаем работать с сущностями в режиме изменений, все изменения происходят на стороне, потом результат записывается в Entity и сохраняется, при чём как можно меньшими порциями. В таком режиме профита от full-proxy вряд ли получится достичь. В тех случаях когда «очень нужно», выключается автотрекинг и сигналы об изменениях пропихиваются вручную. Тоже очень быстро.

Но за информацию спасибо, крайне познавательно!

Gluck_13С другими объявлениями коллекций (например, IList<T>), на первый взгляд, вроде все работает, а потом начинают вылезать глюки, типа когда в контексте присваиваешь новому элементу из дочерней коллекции ссылку на головную сущность, она автоматически не добавляется в коллекцию головы.

Ага, но ещё и надо понимать для чего нужен IList<> и стараться всегда юзать максимально обобщённые коллекции. IList<> нужен, если конкретно требуется индексированный доступ.

В общем, тоже согласен, что надо акцентировать на этом внимание, так как очень многие грешат и IList и даже List ))
...
Рейтинг: 0 / 0
25 сообщений из 25, страница 1 из 1
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / EF Code First - не внести данные в связанную таблицу
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]