2

내 응용 프로그램에서 Entity Framework 4.0 Model First를 사용하고 있습니다. Product을 기본 테이블로 사용하고 SpecificProduct을 Product에서 상속 받도록 상속을 구현하는 여러 테이블이 있습니다.Entity Framework 4.0 모델 데이터베이스에서 모델을 업데이트 한 후 첫 번째 상속이 손실된다.

상속은 edmx 파일에서 볼 수 있습니다. 처음에는 데이터베이스를 설정하기 위해 디자이너를 마우스 오른쪽 버튼으로 클릭하고 SQL 작성 스크립트를 생성하는 "모델에서 데이터베이스 생성"을 선택합니다.

SQL 데이터베이스에서 SpecificProduct 테이블로 일부 변경을 수행하고 모델을 업그레이드하려고한다고 가정 해 봅시다. 분명히 내 edmx 파일에서 테이블 SpecificProduct을 삭제하면 edmx 디자이너에서 rightclick하고 "데이터베이스에서 모델 업데이트"를 선택합니다.
이 결과는 ProductSpecificProduct 사이의 상속이 손실됩니다. 상속 대신에 나는 1에서 0..1의 관계를 가지며 Product의 기본 키는 이제 SpecificProduct의 열이되었습니다.
사실 내 프로젝트가 더 이상 빌드되지 않기 때문에 원하는 방식이 아닙니다. 왜냐하면 내 코드는 상속을 사용할 수 있어야하기 때문입니다.

삽입 된 기본 키 열을 SpecificProduct으로 삭제하고 새로운 1 대 0..1 관계를 삭제하고 edmx 디자이너에서 상속을 다시 삽입하여 디자이너 파일에서 수동으로 수정할 수 있습니다.
자동으로이 작업을 수행 할 방법이 없습니까?

또는이 사실은 모델 첫 번째 시도의 제한 사항이며, 내가 인식하지 못했을뿐입니다 (실제로 다시 제한하지 않으면 다시 선택하지 않음)?

답변

3

EDMX 파일에서 수동으로 어떤 것도 삭제하면 안됩니다. 삭제하면 매핑이 손실됩니다. 상속은 데이터베이스 계층에 대한 지식이 없기 때문에 항상 수동으로 매핑해야합니다. 항상 start with basic relations을 삭제하고 상속으로 변경해야합니다.

그래서 귀하의 경우 엔티티를 삭제하지 않고 데이터베이스에서 업데이트를 실행하십시오. 새 열은 엔티티의 속성으로 추가되어야합니다. Btw. 모델 우선과 데이터베이스 사이의 스와핑은 없습니다. 첫 번째 접근법 또는 두 번째 접근법을 사용하십시오. 이들을 결합하는 것은 지원되지 않습니다.

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을 조정하여 연관에 의해 참조 된 엔티티를 상속 된 객체.

이렇게하기위한 코드는 다음과 같습니다. 당신이 당신을 위해 작동하기 위해 수행해야하는 유일한 것은 라인에 기본 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 파일입니다.

+0

이것은 훌륭한 해결책입니다. 실제로는 edmx 파일을 변경하거나 DB로 푸시 할 수없는 실제 예제를 제공합니다. 우리는이 일을 담당하는 검사를 가지고 있습니다. –

+0

@ kevinx007 : 잠시 전에 게시 했으므로 약간 오래된 것입니다. 이후 SQL Server 데이터베이스 연결에서 EDMX v5 파일을 만드는 데 사용하는 새 라이브러리를 만들었습니다 ... 저에게 훌륭하게 작동합니다. – BG100

+0

다음 주셔서 감사합니다. 라이브러리 기반 솔루션과 관련하여 좀 더 자세하게 설명 할 수 있습니까? 필자는 Autogen 파일을 수정하기를 주저합니다 (EF의 새 버전이 나오고 변경 될 경우). –

관련 문제