Модель Entity Framework 4.0 Первое наследование теряется после обновления модели из базы данных. - PullRequest
2 голосов
/ 06 сентября 2011

В моем приложении я использую Entity Framework 4.0 Model First. У меня есть несколько таблиц, которые реализуют наследование, например Product в качестве базового и SpecificProduct, который наследуется от Product.

Наследование видно в файле edmx. Для первоначальной настройки базы данных я щелкаю правой кнопкой мыши на конструкторе и выбираю «Создать базу данных из модели», которая создает сценарий создания SQL.

Давайте предположим, что я внесу некоторые изменения в базу данных SQL в таблицу SpecificProduct и хочу обновить модель. Очевидно, я удалил бы таблицу SpecificProduct из моего файла edmx и щелкнул правой кнопкой мыши на конструкторе edmx и выбрал «Обновить модель из базы данных».
Это приводит к потере наследства между Product и SpecificProduct. Вместо наследования у меня есть отношение от 1 до 0..1, а первичный ключ Product теперь также является столбцом SpecificProduct.
На самом деле это не так, как я хочу, потому что мой проект больше не будет собираться, потому что мой код полагается на доступное наследование.

Я могу вручную исправить это в файле конструктора, удалив вставленный столбец первичного ключа в SpecificProduct, удалив новое отношение 1 к 0..1 и снова вставив наследование в конструктор edmx. Разве нет способа сделать это автоматически?

Или это просто ограничение попытки Model First, о которой я не знал (и не выбрал бы снова, если это действительно ограничение)?

Ответы [ 2 ]

3 голосов
/ 06 сентября 2011

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

Так что в вашем случае попробуйте просто запустить обновление из базы данных, не удаляя вашу сущность.Новый столбец должен быть добавлен в качестве свойства вашей сущности.Btw.нет замены между моделью сначала и базой данных сначала.Используйте первый подход или второй подход.Объединение их не поддерживается.

1 голос
/ 04 июля 2012

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

Создайте базу данных

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

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

pkPeople           - Primary key constraint
fkPersonAddress    - Foreign key to the Address table
inInstructorPerson - Foreign key representing inheritance
ckPersonAge        - Check constraint

Создание модели объекта «данные»

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

Единственное, что вам нужно изменить, - это удалить EntityModelCodeGenerator из свойства Custom Tool файла edmx.

Добавить текстовый шаблон

Затем вы добавляете новый текстовый шаблон (файл .tt) в ваш проект. Работа этого текстового шаблона состоит в том, чтобы просмотреть файл edmx на основе XML, созданный на предыдущем шаге, и найти все ассоциации, которые начинаются с префикса «in», и настроить XML, как требуется, чтобы сделать объекты, на которые ссылается ассоциация, унаследованные объекты.

Мой код для этого следующий. Единственное, что вам нужно сделать, чтобы заставить его работать, - это изменить жестко закодированное имя файла вашего базового файла EDMX в строке 10.

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".edmx" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#
    var edmx = XDocument.Load(this.Host.ResolvePath("MyData.edmx"));
    var edmxns = edmx.Root.Name.Namespace;

    var csdl = edmx.Root.Element(edmxns + "Runtime").Element(edmxns + "ConceptualModels");
    var csdlSchema = csdl.Elements().First();
    var csdlns = csdlSchema.Name.Namespace;
    var modelns = csdlSchema.Attribute("Namespace").Value;
    var InheritiedObjects = new List<InheritedObject>();

    // GET LIST OF INHERITS
    foreach (var a in csdlSchema.Elements(csdlns + "Association").Where(ca => ca.Attribute("Name").Value.StartsWith("in"))) {
        InheritedObject io = new InheritedObject() { ForeignKey = a.Attribute("Name").Value };

        try {
            io.QualifiedParent = a.Elements(csdlns + "End").Single(cae => cae.Attribute("Multiplicity").Value == "1").Attribute("Type").Value;
            io.QualifiedChild = a.Elements(csdlns + "End").Single(cae => cae.Attribute("Multiplicity").Value == "0..1").Attribute("Type").Value;
            InheritiedObjects.Add(io);

        } catch {
            Warning("Foreign key '" + io.ForeignKey + "' doesn't contain parent and child roles with the correct multiplicity.");
        }   
    }

    // SET ABSTRACT OBJECTS
    foreach (var ao in InheritiedObjects.Distinct()) {
        WriteLine("<!-- ABSTRACT: {0} -->", ao.Parent);
        csdlSchema.Elements(csdlns + "EntityType")
            .Single(et => et.Attribute("Name").Value == ao.Parent)
            .SetAttributeValue("Abstract", "true");
    }
    WriteLine("<!-- -->");

    // SET INHERITANCE
    foreach (var io in InheritiedObjects) {

        XElement EntityType = csdlSchema.Elements(csdlns + "EntityType").Single(cet => cet.Attribute("Name").Value == io.Child);
        WriteLine("<!-- INHERITED OBJECT: {0} -->", io.Child);

        // REMOVE THE ASSOCIATION SET
        csdlSchema.Element(csdlns + "EntityContainer")
            .Elements(csdlns + "AssociationSet")
            .Single(cas => cas.Attribute("Association").Value == modelns + "." + io.ForeignKey)
            .Remove();
        WriteLine("<!--     ASSOCIATION SET {0} REMOVED -->", modelns + "." + io.ForeignKey);

        // REMOVE THE ASSOCIATION
        csdlSchema.Elements(csdlns + "Association")
            .Single(ca => ca.Attribute("Name").Value == io.ForeignKey)
            .Remove();
        WriteLine("<!--     ASSOCIATION {0} REMOVED -->", io.ForeignKey);

        // GET THE CHILD ENTITY SET NAME
        io.ChildSet = csdlSchema.Element(csdlns + "EntityContainer")
            .Elements(csdlns + "EntitySet")
            .Single(es => es.Attribute("EntityType").Value == io.QualifiedChild)
            .Attribute("Name").Value;

        // GET THE PARENT ENTITY SET NAME
        io.ParentSet = csdlSchema.Element(csdlns + "EntityContainer")
            .Elements(csdlns + "EntitySet")
            .Single(es => es.Attribute("EntityType").Value == io.QualifiedParent)
            .Attribute("Name").Value;

        // UPDATE ALL ASSOCIATION SETS THAT REFERENCE THE CHILD ENTITY SET
        foreach(var a in csdlSchema.Element(csdlns + "EntityContainer").Elements(csdlns + "AssociationSet")) {
            foreach (var e in a.Elements(csdlns + "End")) {
                if (e.Attribute("EntitySet").Value == io.ChildSet) e.SetAttributeValue("EntitySet", io.ParentSet);
            }
        }           

        // REMOVE THE ENTITY SET
        csdlSchema.Element(csdlns + "EntityContainer")
            .Elements(csdlns + "EntitySet")
            .Single(es => es.Attribute("EntityType").Value == io.QualifiedChild)
            .Remove();
        WriteLine("<!--     ENTITY SET {0} REMOVED -->", io.QualifiedChild);

        // SET BASE TYPE
        EntityType.SetAttributeValue("BaseType", io.QualifiedParent);
        WriteLine("<!--     BASE TYPE SET TO {0} -->", io.QualifiedParent);

        // REMOVE KEY
        EntityType.Element(csdlns + "Key").Remove();
        WriteLine("<!--     KEY REMOVED -->");

        // REMOVE ID PROPERTY
        EntityType.Elements(csdlns + "Property")
            .Where(etp => etp.Attribute("Name").Value == "ID")
            .Remove();
        WriteLine("<!--     ID PROPERTY REMOVED -->");

        // REMOVE NAVIGATION PROPERTIES THAT REFERENCE THE OLD ASSOCIATION
        List<XElement> NavList = new List<XElement>();
        foreach (var np in csdlSchema.Descendants(csdlns + "NavigationProperty")) {
            if (np.Attribute("Relationship").Value == modelns + "." + io.ForeignKey) {
                WriteLine("<!--     REMOVING NAVIGATION PROPERTY {0} FROM {1} -->", np.Attribute("Name").Value, np.Parent.Attribute("Name").Value);
                NavList.Add(np);

            }
        }
        NavList.ForEach(n => n.Remove());

        // REMOVE NAVIGATION PROPERTIES FROM THE PARENT THAT POINTS TO A FOREIGN KEY OF THE CHILD
        foreach (var np in EntityType.Elements(csdlns + "NavigationProperty")) {
            csdlSchema.Elements(csdlns + "EntityType")
                .Single(cet => cet.Attribute("Name").Value == io.Parent)
                .Elements(csdlns + "NavigationProperty")
                .Where(pet => pet.Attribute("Name").Value == np.Attribute("Name").Value)
                .Remove();
        }

        WriteLine("<!-- -->");
    }

    Write(edmx.ToString());



#>
<#+
    public class InheritedObject : IEquatable<InheritedObject> {
        public string ForeignKey { get; set; }
        public string QualifiedParent { get; set; }
        public string QualifiedChild { get; set; }          
        public string Parent { get { return RemoveNamespace(QualifiedParent); } }
        public string Child { get { return RemoveNamespace(QualifiedChild); } }
        public string ParentSet { get; set; }
        public string ChildSet { get; set; }

        private string RemoveNamespace(string expr) {
            if (expr.LastIndexOf(".") > -1) 
                return expr.Substring(expr.LastIndexOf(".") + 1);
            else
                return expr;
        }

        public bool Equals(InheritedObject other) {
            if (Object.ReferenceEquals(other, null)) return false;
            if (Object.ReferenceEquals(this, other)) return true;
            return QualifiedParent.Equals(other.QualifiedParent);
        }

        public override int GetHashCode() {
            return QualifiedParent.GetHashCode();
        }
    }
#>

Результаты

Текстовый шаблон создаст новый файл .edmx (как подфайл из текстового шаблона). Это ваш последний файл .edmx, в котором будут содержаться все ваши сущности с правильным наследованием в зависимости от того, как вы назвали ограничения внешнего ключа, которые генерируются автоматически.

...