简体   繁体   English

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

[英]Entity Framework 4.0 Model First inheritance is lost after updating model from database

In my application I'm using Entity Framework 4.0 Model First. 在我的应用程序中,我首先使用Entity Framework 4.0 Model。 I have several tables that implement inheritance, like Product as a basetable and SpecificProduct that inherits from Product. 我有几个实现继承的表,例如Product作为基本表和从Product继承的SpecificProduct

The inheritance is visible in the edmx file. 继承在edmx文件中可见。 To initially set up the database, I rightclick on the designer and select "Generate Database from Model", which generates an SQL create script. 要最初设置数据库,我右键单击设计器,然后选择“从模型生成数据库”,这将生成一个SQL创建脚本。

Let's assume I do some changes in the SQL database to the SpecificProduct table and want to upgrade the model. 假设我在SQL数据库中对SpecificProduct表做了一些更改,并想升级模型。 Obviously I'd delete the table SpecificProduct from my edmx file and I'd rightclick on the edmx designer and select "Update Model from Database". 显然,我将从edmx文件中删除表SpecificProduct ,然后右键单击edmx设计器,然后选择“从数据库更新模型”。
This results in the inheritance between Product and SpecificProduct being lost. 这将导致ProductSpecificProduct之间的继承丢失。 Instead of the inheritance I do have a 1 to 0..1 relationship and the primary key of Product is now also a column of SpecificProduct . 我没有继承关系,而是有1到0..1的关系,并且Product的主键现在也是SpecificProduct的列。
This is actually not the way I want it to be, because my project won't build anymore, because my code relies on the inheritance being available. 实际上,这不是我想要的方式,因为我的项目不再构建,因为我的代码依赖于可用的继承。

I can manually fix this in the designer file by deleting the inserted primary key column into SpecificProduct , deleting the new 1 to 0..1 relationship, and inserting the inheritance in the edmx designer, again. 我可以在设计器文件中手动修复此问题,方法是将插入的主键列删除到SpecificProduct ,删除新的1到0..1关系,然后再次在edmx设计器中插入继承。
Ain't there a way to do this automatically? 没有办法自动执行此操作吗?

Or is this simply a limition of the Model First attempt, that I wasn't aware of (and wouldn't choose again, if this really is a limitation)? 还是这仅仅是我没有意识到的Model First尝试的局限性(如果这确实是一个局限性,就不会再选择)?

You must not manually delete anything from EDMX file. 您不得手动从EDMX文件中删除任何内容。 Once you delete it your mapping is lost. 一旦删除它,您的映射就会丢失。 The inheritance must be always mapped manually because database layer has no knowledge about it. 必须始终手动映射继承,因为数据库层对此不了解。 You always start with basic relations which you must delete and change it to inheritance. 您总是从基本关系开始,必须删除基本关系并将其更改为继承。

So in your case try to simply run update from database without deleting your entity. 因此,在您的情况下,请尝试仅从数据库运行更新而不删除您的实体。 The new column should be added as a property to your entity. 新列应作为属性添加到您的实体。 Btw. 顺便说一句。 there is no swapping between model first and database first. 在模型优先和数据库优先之间没有交换。 Use the first approach or second approach. 使用第一种方法或第二种方法。 Combining them is not supported. 不支持将它们组合。

I was after a solution to this same problem. 我一直在寻找解决这个问题的方法。 I managed to achieve a very effective method of applying the inheritance changes by using Text Templates. 我设法通过使用文本模板实现了一种非常有效的应用继承更改的方法。 Here's how... 这是如何做...

Create your database 创建你的数据库

First, create your database as normal except you need to name the foreign key constraints that represent the inheritance in a different way to the other foreign keys. 首先,正常创建数据库,除了需要命名外键约束以与其他外键不同的方式表示继承。

The naming convention I use for constraints is a two letter prefix like this: 我用于约束的命名约定是两个字母的前缀,如下所示:

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

Generate the 'data' entity model 生成“数据”实体模型

Next, you create a new Entity Data Model and generate it from the database. 接下来,创建一个新的实体数据模型并从数据库中生成它。 Don't modify anything at all in this EDMX file, it needs to stay exactly as it was generated. 完全不要修改此EDMX文件中的任何内容,它必须保持与生成时完全相同。 That way, if you ever need to make major changes to the database, you can just delete it and recreate it. 这样,如果您需要对数据库进行重大更改,则可以删除它并重新创建它。

The only thing you need to change is to delete 'EntityModelCodeGenerator' from the Custom Tool property of the edmx file. 您唯一需要更改的就是从edmx文件的“自定义工具”属性中删除“ EntityModelCodeGenerator”。

Add the Text Template 添加文本模板

Then you add a new Text Template (.tt file) to your project. 然后,将新的文本模板(.tt文件)添加到您的项目。 The job of this Text Template is to look through the XML based edmx file created in the previous step and look for all the associations that start with the prefix 'in', and tweak the XML as required to make the entities referenced by the association into inherited objects. 该文本模板的工作是浏览上一步中创建的基于XML的edmx文件,并查找所有以前缀“ in”开头的关联,并根据需要对XML进行调整,以使关联所引用的实体成为继承的对象。

My code for doing this follows. 我的代码如下。 The only thing you'll need to do to make it work for you is to change the hard coded filename of your base EDMX file on line 10. 要使它正常工作,您唯一需要做的就是在第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();
        }
    }
#>

Results 结果

The Text Template will create a new .edmx file (as a sub file from the Text Template). 文本模板将创建一个新的.edmx文件(作为文本模板的子文件)。 This is your final .edmx file that will contain all your entities with the correct inheritance based on how you named your foreign key constraints, which is fully auto-generated. 这是您最终的.edmx文件,它将基于您如何命名外键约束(完全自动生成)来包含所有具有正确继承的实体。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 实体框架上下文和 model inheritance 在数据库第一种方法中搭建脚手架时 - Entity Framework context and model inheritance when scaffolding in database first approach 实体框架数据库优先-模型中的继承问题 - Entity Framework DB First - Problems with inheritance in the model 更新数据库和更改模型后实体框架验证错误 - Entity framework validation error after updating database and changing model 错误:使用实体框架从数据库更新模型 - Error: Updating Model from the database using Entity Framework 当我从现有数据库创建模型时,Entity Framework 4.0 生成只读模型 - Entity Framework 4.0 generating read only model when I create model from existing database 使用 Entity Framework 6.1 和 MVC 5 从数据库使用 Code First 后如何同步模型? - How to sync model after using Code First from Database using Entity Framework 6.1 and MVC 5? 处置上下文后,如何从实体框架(数据库优先)返回包含导航属性的模型? - How do I return a Model from Entity Framework (Database First) that includes the Navigation Properties after the context is disposed? 将数据库从嵌入式更改为SQL Server Express后,Entity Framework 4.1代码第一个模型兼容性问题 - Entity Framework 4.1 code first model compatibility issue after changing database from embedded to SQL Server Express 来自模型的Entity Framework数据库 - Entity Framework database from a model 数据库设计中的实体框架代码优先模型 - Entity Framework Code First model from Database design
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM