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

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

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

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

Не удалось найти имя EntitySet «DataEntities.Employer».

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

Не удалось определить допустимый порядок зависимых операций. Зависимости могут существовать из-за ограничений внешнего ключа, требований модели или хранения сгенерированных значений.

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

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


person ChrisHDog    schedule 17.10.2008    source источник


Ответы (5)


Меня зовут Фани, и я работаю в команде ADO.NET Data Services.

Методы 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 Entity Type после отправки изменений на сервер, мы ожидаем, что информация о типе будет присутствовать в полезной нагрузке, когда сущности участвуют в наследовании.

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);
}
person Phani Raj    schedule 15.12.2008

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

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

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

person Luhmann    schedule 17.10.2008

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

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

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

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

person ChrisHDog    schedule 22.10.2008

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

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

Я не уверен, есть ли какое-то более обычное решение.

person Nenad Dobrilovic    schedule 17.10.2008

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

Не уверен насчет более ранних версий, но 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()-, или что у вас должен быть какой-то другой способ передачи изменений объекта в хранилище данных.

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

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