实体框架4.0模型从数据库更新模型后,首先继承丢失



在我的应用程序中,我首先使用实体框架4.0模型。我有几个实现继承的表,如Product作为一个基表,SpecificProduct继承自Product。

继承在edmx文件中可见。为了初始设置数据库,我右键单击设计器并选择"从模型生成数据库",这将生成一个SQL创建脚本。

让我们假设我在SQL数据库中对SpecificProduct表进行了一些更改,并希望升级模型。显然,我会从我的edmx文件中删除表SpecificProduct,然后右键单击edmx设计器并选择"从数据库更新模型"。
这将导致ProductSpecificProduct之间的继承丢失。而不是继承,我有一个1到0…1的关系和Product的主键现在也是SpecificProduct的一列。
这实际上不是我想要的方式,因为我的项目将不再构建,因为我的代码依赖于可用的继承。

我可以通过将插入的主键列删除到SpecificProduct中,删除新的1到0,在设计器文件中手动修复此问题。1关系,并再次在edmx设计器中插入继承。
没有自动完成的方法吗?

或者这只是模型第一尝试的限制,我没有意识到(如果这真的是一个限制,我不会再选择一次)?

不能手动删除EDMX文件中的任何内容。一旦删除它,映射就丢失了。继承必须始终手动映射,因为数据库层不知道它。您总是从必须删除的基本关系开始,并将其更改为继承。

所以在你的情况下,尝试简单地运行update from database而不删除你的实体。应该将新列作为属性添加到实体中。顺便说一句。模型优先和数据库优先之间没有交换。使用第一种方法或第二种方法。

我在寻找同样问题的解决方案。我设法通过使用文本模板实现了一种非常有效的应用继承更改的方法。这里有…

创建数据库

首先,像往常一样创建数据库,只是需要以与其他外键不同的方式命名表示继承的外键约束。

我对约束使用的命名约定是一个像这样的两个字母前缀:

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

生成'data'实体模型

接下来,创建一个新的实体数据模型,并从数据库生成它。不要修改这个EDMX文件中的任何内容,它需要完全保持生成时的样子。这样,如果您需要对数据库进行重大更改,您可以删除它并重新创建它。

您唯一需要更改的是从edmx文件的自定义工具属性中删除'EntityModelCodeGenerator'。

添加文本模板

然后添加一个新的Text Template(。(文件)到您的项目。这个文本模板的任务是查看在上一步中创建的基于XML的edmx文件,查找所有以前缀'in'开头的关联,并根据需要调整XML,使关联引用的实体成为继承的对象。

我这样做的代码如下。您需要做的唯一一件事就是在第10行更改基本EDMX文件的硬编码文件名。

<#@ 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文件,它将包含所有实体,并根据您如何命名外键约束来正确继承,这是完全自动生成的。

最新更新