Entity Framework, моделирование Code First и циклическая ссылка - PullRequest
8 голосов
/ 08 августа 2011

Я провел несколько дней, пытаясь решить эту проблему. Делая простой проект, иллюстрирующий мою проблему, я наткнулся на возможное решение. Итак, это своего рода двойной вопрос.

Но сначала небольшая справочная информация:

Я только начал использовать Entity Framework 4.1 (EF) и Code First для создания моделей для моего проекта ASP.NET MVC. Мне нужно несколько моделей, подобных этой:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class Family
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Father> Fathers { get; set; }
        public virtual ICollection<Mother> Mothers { get; set; }
    }

    public class Mother
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Father
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Child
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int MotherID { get; set; }
        public int FatherID { get; set; }

        public virtual Mother Mother { get; set; }
        public virtual Father Father { get; set; }
    }
}

И DbContext:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;

namespace TestApp.Models
{
    public class TestContext : DbContext
    {
        public DbSet<Family> Families { get; set; }
        public DbSet<Mother> Mothers { get; set; }
        public DbSet<Father> Fathers { get; set; }
        public DbSet<Child> Children { get; set; }
    }
}

(Пожалуйста, извините за неудачный пример, вот что смог придумать мой пятничный жареный мозг.)

В семье может быть несколько матерей и несколько отцов. И у ребенка есть мать и отец. Я проверил одного из гуру .NET на моей работе, который согласился, что в этом нет ничего экстраординарного. По крайней мере, насколько мы можем видеть.

Но когда я запускаю код, я получаю это исключение:

System.Data.SqlServerCe.SqlCeException: ссылочная связь приведет к циклической ссылке, которая не разрешена. [Имя ограничения = Mother_Family]

Я вижу цикл: Family - Mother - Child - Father - Family. Но если бы я сам создал таблицы базы данных (что я предпочитаю не делать, это то, что мне нравится в Code First), то, насколько я могу судить, это была бы совершенно правильная структура данных.

Итак, мой первый вопрос: Почему это проблема при первом использовании кода? Есть ли способ рассказать EF, как правильно обрабатывать цикл?

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestApp.Models
{
    public class Family
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Father> Fathers { get; set; }
        public virtual ICollection<Mother> Mothers { get; set; }
    }

    public class Mother
    {
        public int ID { get; set; }
        public string Name { get; set; }
        // public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Father
    {
        public int ID { get; set; }
        public string Name { get; set; }
        // public int FamilyID { get; set; }

        public virtual ICollection<Child> Children { get; set; }
        public virtual Family Family { get; set; }
    }

    public class Child
    {
        public int ID { get; set; }
        public string Name { get; set; }
        // public int MotherID { get; set; }
        // public int FatherID { get; set; }

        public virtual Mother Mother { get; set; }
        public virtual Father Father { get; set; }
    }
}

Итак, удаление этих SomethingID справочных свойств, похоже, решает мою проблему. Как вы можете видеть в контроллере примера проекта, на который я ссылаюсь в конце этого поста, я все еще могу циклически повторяться и делать что-то вроде mothers.First().Family.Fathers.First().Children.First().Mother.Family.Name без каких-либо проблем. Но все учебные пособия и примеры EF и Code First моделирования, на которые я смотрел (например, от Scott Guthrie ), включают эти свойства, поэтому кажется неправильным не использовать их.

Итак, мой второй вопрос: Будут ли какие-либо недостатки и проблемы, которые я еще не обнаружил при выполнении этого?

Загрузите пример проекта здесь: http://blackfin.cannedtuna.org/cyclical-reference-test-app.zip, и откройте TestSolution.sln. Свойства закомментированы в примере проекта. Раскомментируйте строки в TestModels.cs, чтобы добавить свойства, что приведет к исключению циклической ссылки.

NB: Решение заключается в создании и заполнении базы данных SQL CE, расположенной по адресу c: \ TestApp.sdf

Обновление, декабрь 2011 г .: Я никогда не решал эту проблему технически, но я бросил свою работу и нашел другую работу, где мне не нужно использовать технологии Microsoft. Такого рода решенная моя проблема:)

В качестве технической поддержки в старом месте, когда пишут при устранении проблем: «Обходной путь или решение предоставлено».

Ответы [ 3 ]

3 голосов
/ 08 августа 2011

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

Это то, что вы должны перепроверить. Исключение исходит непосредственно из базы данных, а не из Entity Framework. Вполне вероятно, что структура таблицы с теми же ограничениями, созданными вручную, будет недействительной. Имейте в виду, что ваши свойства внешнего ключа Mother.FamilyID, Father.FamilyID, Child.MotherID и Child.FatherID не могут иметь значения NULL , поэтому они представляют обязательные отношения, и соответствующие столбцы в базе данных также не могут иметь значение NULL.

Когда вы удаляете все эти свойства из классов вашей модели, ваши отношения внезапно становятся необязательными , потому что свойства навигации могут быть null. Теперь это еще одна модель, поскольку столбцы FK в БД могут иметь значение NULL! Видимо это разрешенная модель.

Если вы хотите иметь в своей модели все еще свойства внешнего ключа, которые представляют необязательные, а не обязательные отношения, вы можете использовать обнуляемые типы: public int? FamilyID { get; set; }, public int? MotherID { get; set; } и т. Д.

0 голосов
/ 29 апреля 2013

У меня была почти такая же проблема, однако я решил ее, используя рекомендации, приведенные в этом ответе Код Entity Framework First - два внешних ключа из одной таблицы , что работает лучше, чем изменение типа столбцов ключей на необязательные.

0 голосов
/ 08 августа 2011

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

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

...