我正在寻求解决同样问题的方法。我设法通过使用文本模板实现了一种非常有效的应用继承更改的方法。就是这样...
创建你的数据库
首先,照常创建数据库,但您需要以与其他外键不同的方式命名表示继承的外键约束。
我用于约束的命名约定是两个字母的前缀,如下所示:
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 文件,它将包含您的所有实体,并根据您命名外键约束的方式正确继承,这是完全自动生成的。