Невозможно сохранить унаследованные типы Entity Framework

Я реализовал в своей модели данных некоторое наследование по типам таблиц (в основном это тип BaseEntity со всей базовой информацией для моих элементов и тип Employer, который наследуется от элемента BaseEntity). Кажется, что все настроено правильно, и при использовании Entities (либо через ADO.net Data Services, либо через Linq to Entities) я вижу тип Employer, и все в порядке. Проблема начинается, когда я создаю новый объект Employer и пытаюсь его сохранить.

В контексте, который не является элементом .AddToEmployer (только и AddObject или AddToBaseEntity).

Если я использую AddObject("Employer", NewEmployer), я получаю сообщение об ошибке:

The EntitySet name 'DataEntities.Employer' could not be found.

Если я использую AddToBaseEntity(NewEmployer), я получаю сообщение об ошибке:

Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements orstore generated values.

Пропустил ли я шаг в настройке наследования? Есть ли какой-то особый способ сохранить унаследованные объекты? Что я делаю не так? Я предполагаю, что основная проблема заключается в том, что у меня должен быть AddToEmployer, что мне нужно сделать, чтобы это было открыто? Кажется странным, что это не вариант, поскольку я вижу тип работодателя на стороне клиента и могу делать такие вещи, как:

var NewEmployer = new Employer() - что, кажется, наводит на мысль, что я вижу тип работодателя в порядке.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
14 340
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

У вас нет работодателя, определенного как набор сущностей, как и для типа сущности. Таким образом, вам не хватает AddToEntity в объекте контекста. Всегда существует один набор сущностей для одной иерархии классов, в данном случае это набор сущностей BaseClass.

Если вы хотите получить набор сущностей «Работодатель», вы можете попробовать вручную отредактировать файл edmx и добавить новый набор сущностей «Работодатель», а затем установить тип сущности «Работодатель» как принадлежащий этому набору сущностей. Это не должно быть сложно, я делал это много раз.

Не уверен, есть ли более обычное решение.

Ну вы получаете только набор сущностей pr. базовый класс, поэтому .AddToBaseEntity - это решение как таковое.

Но похоже, что у вас есть циклическая зависимость в вашей модели, поэтому инфраструктура Entity не может определить, в каком порядке сохранять.

Убедитесь, что у вас есть внешние ключи к базовому объекту на производных объектах, и обновите модель.

Ответ принят как подходящий

Я изменил пару вещей и смог заставить это работать. Я не совсем уверен, в чем была основная проблема, но хотел опубликовать то, что я сделал, для справки.

Восстановленные таблицы: я перестроил таблицы, начиная только с столбцов ID / Key и одного столбца данных.

Удалены лишние поля с автоинкрементом: у меня был ID с автоинкрементом в BaseEntity и у работодателя. Я удалил автоматически увеличивающийся идентификатор на работодателе и только что получил столбец Employer.BaseEntityID и внешний ключ обратно в BaseEntity.BaseEntityID. (похоже, это было виновником, но у меня сложилось впечатление, что это разрешено)

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

Меня зовут Фани, я работаю в группе служб данных ADO.NET.

Методы ResolveName и ResolveType призваны помочь вам настроить информацию о типе, которую клиент записывает в полезную нагрузку, отправляемую на сервер, и способ материализации полезной нагрузки ответа от сервера.

Они помогают вам определять типы на клиенте и полезны во многих сценариях. Вот несколько примеров:

  1. Иерархия типов сущностей на клиенте отличается от серверной.
  2. Типы сущностей, предоставляемые службой, участвуют в наследовании, и вы хотите работать с производными типами на клиенте.

ResolveName используется для изменения имени объекта, который мы помещаем в сеть при отправке запроса на сервер.

Рассмотрим эту модель данных: На сервере

public class Employee {
    public int EmployeeID {get;set;}
    public string EmployeeName {get;set;}
}

public class Manager:Employee {
    public List<int> employeesWhoReportToMe {get;set;}
}

Когда вы используете клиент для работы с экземплярами типа объекта Manager, после отправки изменений на сервер мы ожидаем, что информация о типе будет присутствовать в полезной нагрузке, когда сущности участвуют в наследовании.

context.AddObject("Employees",ManagerInstance ); <-- add manager instance to the employees set.
context.SaveChanges();

Однако, когда клиент сериализует эту полезную нагрузку, он вставляет «Сотрудник» в качестве имени типа. что не то, что ожидается на сервере. Следовательно, вы должны предоставить преобразователь имен на клиенте,

context.ResolveName = delegate(Type entityType){
    //do what you have to do to resolve the type to the right type of the entity on the server
    return entityType.FullName;
}

преобразователь типов используется таким же образом.

context.ResolveType = delegate(string entitySetName){
    //do what you have to do to convert the entitysetName to a type that the client understands
    return Type.GetType(entitySetName);
}

Спустя два года, но в интересах сохранения актуальности для поискового трафика, я быстро обошел этот способ в удобном классе, который мы использовали для быстрого заполнения нашей базы данных промежуточными данными.

Не уверен в более ранних версиях, но Entity Framework 4 позволяет вам выгружать объект в контекст как базовый объект, а затем платформа определяет ссылки на стороне сервера. Таким образом, вы не использовали бы AddToInheritedObjects () (который в любом случае устарел), а скорее метод ObjectSet <>. Add ().

Вот пример вспомогательного класса:

public ContextHelper
{
        …
        _context = ModelEntities();

        public T Create<T>() where T : class
        {
            // Create a new context
            _context = new ModelEntities();

            // Create the object using the context (can create outside of context too, FYI)
            T obj = _context.CreateObject<T>();

            // Somewhat kludgy workaround for determining if the object is
            // inherited from the base object or not, and adding it to the context's
            // object list appropriately.    
            if (obj is BaseObject)
            {
                _context.AddObject("BaseObjects", obj);
            }
            else
            {
                ObjectSet<T> set = _context.CreateObjectSet<T>();
                set.AddObject(obj);
            }

            return obj;
        }
        …
}

Таким образом, если у вас есть следующее:

class ObjectOne : BaseObject {}
class ObjectTwo {}

Вы можете легко добавить сущности в контекст:

ContextHelper ch = ContextHelper()
ObjectOne inherited = ch.Create<ObjectOne>();
ObjectTwo toplevel = ch.Create<ObjectTwo>();
…

Помня, конечно, что ContextHelper должен иметь общедоступный метод Save (), который вызывает _context.SaveChanges () - или что у вас должен быть другой способ передачи изменений объекта в хранилище данных.

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

В качестве примечания, которое я забыл упомянуть, реализация IDisposable делает это действительно приятным.

JohnMetta 23.12.2010 23:16

Другие вопросы по теме