实体框架4.0模型从数据库更新模型后,第一次继承丢失

时间:2011-09-06 07:46:08

标签: c# asp.net sql-server-2008 entity-framework-4 ef-model-first

在我的应用程序中,我正在使用Entity Framework 4.0 Model First。 我有几个实现继承的表,例如Product作为基础,而SpecificProduct继承自Product。

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

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

我可以通过将插入的主键列删除到SpecificProduct,删除新的1到0..1关系,再次在edmx设计器中插入继承,在设计器文件中手动修复此问题。 /> 有没有办法自动执行此操作?

或者这仅仅是模型优先尝试的限制,我不知道(如果这确实是一个限制,也不会再次选择)?

2 个答案:

答案 0 :(得分:3)

您不能手动删除EDMX文件中的任何内容。删除后,您的映射将丢失。必须始终手动映射继承,因为数据库层不了解它。您始终start with basic relations必须删除并将其更改为继承。

因此,在您的情况下,尝试简单地从数据库运行更新而不删除您的实体。新列应作为属性添加到您的实体。顺便说一句。首先是模型和数据库之间没有交换。使用第一种方法或第二种方法。不支持组合它们。

答案 1 :(得分:1)

我解决了同样的问题。我设法通过使用文本模板实现了一种非常有效的应用继承更改的方法。这是怎么......

创建数据库

首先,正常创建数据库,除非您需要以与其他外键不同的方式命名表示继承的外键约束。

我用于约束的命名约定是一个双字母前缀,如下所示:

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

生成“数据”实体模型

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

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

添加文字模板

然后向项目添加新的文本模板(.tt文件)。此文本模板的工作是查看在上一步中创建的基于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文件,它将包含具有正确继承的所有实体,具体取决于您如何命名外键约束,这是完全自动生成的。