如何在EF4实体上提取属性的数据库表和列名?

我正在为使用EF4进行数据访问层的应用程序编写审计组件。 我能够非常轻松地确定哪些实体已被修改,并且通过ObjectStateEntry对象,我可以提取已修改的原始值,当前值,实体名称和属性名称,但我还想提取原始表格和SQL Server中使用的列名和列名(因为它们并不总是与模型的实体和属性名称匹配)

有谁知道这样做的好方法? 它甚至可能吗? 映射显然存储在MSL中,但我无法找到以编程方式访问这些映射的方法。

所有的模型数据都可以通过这种方法获得myObjectContext.MetadataWorkspace.GetEntityContainer(myObjectContext.DefaultContainerName, DataSpace.CSSpace);

这应该至少可以让你开始如何做你想要的。 DataSpace.CSSpace指定概念名称和商店名称之间的映射。 DataSpace.CSpace为您提供概念模型, DataSpace.SSpace为您提供存储模型。

在查看entity framework模型设计器之后,我看到它使用EdmEntityTypeAttributeDataMemberAttribute来装饰生成的类和属性。 它们中的每一个都有一个Name属性,其中包含映射实体的名称(分别为table,column)。 当属性名称与列的名称匹配时,设计器不会为位置参数Name提供值。 下面的代码对我来说很好。

  private static string GetTableName() where T : EntityObject { Type type = typeof(T); var at = GetAttribute(type); return at.Name; } private static string GetColumnName(Expression> propertySelector) where T : EntityObject { Contract.Requires(propertySelector != null, "propertySelector is null."); PropertyInfo propertyInfo = GetPropertyInfo(propertySelector.Body); DataMemberAttribute attribute = GetAttribute(propertyInfo); if (String.IsNullOrEmpty(attribute.Name)) { return propertyInfo.Name; } return attribute.Name; } private static T GetAttribute(MemberInfo memberInfo) where T : class { Contract.Requires(memberInfo != null, "memberInfo is null."); Contract.Ensures(Contract.Result() != null); object[] customAttributes = memberInfo.GetCustomAttributes(typeof(T), false); T attribute = customAttributes.Where(a => a is T).First() as T; return attribute; } private static PropertyInfo GetPropertyInfo(Expression propertySelector) { Contract.Requires(propertySelector != null, "propertySelector is null."); MemberExpression memberExpression = propertySelector as MemberExpression; if (memberExpression == null) { UnaryExpression unaryExpression = propertySelector as UnaryExpression; if (unaryExpression != null && unaryExpression.NodeType == ExpressionType.Convert) { memberExpression = unaryExpression.Operand as MemberExpression; } } if (memberExpression != null && memberExpression.Member.MemberType == MemberTypes.Property) { return memberExpression.Member as PropertyInfo; } throw new ArgumentException("No property reference was found.", "propertySelector"); } // Invocation example private static Test() { string table = GetTableName(); string column = GetColumnName(u=>u.Name); } 

如果您编写代码来审核映射,那么您是不是真的在审核/validationMicrosoft的EF代码? 也许这可以安全地从问题域中定义,除非审计的目的是建立对EF本身的信心。

但是,如果您确实需要进行这种审核,则可能需要添加一个构建步骤,将.edmx文件作为资源嵌入到您正在检查的DLL中。 你没有说你是否在被测DLL上有这种控制/输入。 然而,这将是一个黑客攻击 – 正如JasCav所说,ORM的目的是使你正在尝试不必要的东西。

这是一个用于在概念和商店信息之间进行转换的通用算法,用Visual Basic 2010编写。

我编写了一个新例程,用于将实体/属性对转换为表/列对。 此类MSLMappingAction在其构造函数中接受模型名称以及XElement XML树,MSL映射文件或XML字符串。 然后,使用ConceptualToStore方法获取String指定实体和属性“表达式”(存储在MSLConceptualInfo结构中)并查找表名和列名(存储在MSLStoreInfo结构中)。

笔记:

  1. 也可以写一个“ StoreToConceptual ”方法转换到另一个方向,但XML查询可能会有点复杂。 处理导航 – function/存储过程映射也是如此。
  2. 注意派生实体的inheritance属性! 如果属性不是派生实体特有的,那么您应该使用基本实体的名称。)

这是代码。

主机代码:(对于给定的XML示例[见底部],当给定实体“Location”和属性表达式“Address.Street”[和]时,它返回表名“Locations”和列名“Address_Street”作为商店信息。概念模型名称“SCTModel”]):

 Dim MSL As MSLMappingAction = New MSLMappingAction(".\SCTModel.msl", "SCTModel") Dim ConceptualInfo As MSLConceptualInfo = New MSLConceptualInfo With {.EntityName = "Location", .PropertyName = "Address.Street"} Dim StoreInfo As MSLStoreInfo = MSL.ConceptualToStore(ConceptualInfo) MessageBox.Show(StoreInfo.TableName & ": " & StoreInfo.ColumnName) 

class级代码:

 Option Infer On Imports System.Xml.Linq '''  ''' This class allows one to convert between an EF conceptual model's entity/property pair ''' and its database store's table/column pair. '''  ''' It takes into account entity splitting and complex-property designations; ''' it DOES NOT take into account inherited properties ''' (in such a case, you should access the entity's base class) Public Class MSLMappingAction ' private fields and routines Private mmaMSLMapping As XElement Private mmaModelName, mmaNamespace As String Private Function FullElementName(ByVal ElementName As String) As String ' pre-pend Namespace to ElementName Return "{" & mmaNamespace & "}" & ElementName End Function Private Sub ValidateParams(ByVal MappingXML As XElement, Byval ModelName As String) ' verify that model name is specified If String.IsNullOrEmpty(ModelName) Then Throw New EntityException("Entity model name is not given!") End If ' verify that we're using CS space If MappingXML.@Space <> "CS" Then Throw New MetadataException("XML is not CS mapping data!") End If ' get Namespace and set private variables mmaNamespace = MappingXML.@xmlns mmaMSLMapping = MappingXML : mmaModelName = ModelName End Sub Private Function IsSequenceEmpty(Items As IEnumerable(Of XElement)) As Boolean ' determine if query result is empty Return _ Items Is Nothing OrElse Items.Count = 0 End Function ' properties '''  ''' Name of conceptual entity model '''  ''' Conceptual-model String ''' Model name can only be set in constructor Public ReadOnly Property EntityModelName() As String Get Return mmaModelName End Get End Property '''  ''' Name of mapping namespace '''  ''' Namespace String of CS mapping layer ''' This value is determined when the XML mapping ''' is first parsed in the constructor Public ReadOnly Property MappingNamespace() As String Get Return mmaNamespace End Get End Property ' constructors '''  ''' Get CS mapping information for an entity model (with XML tree) '''  ''' XML mapping tree ''' Conceptual-model name '''  Public Sub New(ByVal MappingXML As XElement, ByVal ModelName As String) ValidateParams(MappingXML, ModelName) End Sub '''  ''' Get CS mapping information for an entity model (with XML file) '''  ''' MSL mapping file ''' Conceptual-model name '''  Public Sub New(ByVal MSLFile As String, ByVal ModelName As String) Dim MappingXML As XElement = XElement.Load(MSLFile) ValidateParams(MappingXML, ModelName) End Sub ' methods '''  ''' Get CS mapping infomration for an entity model (with XML String) '''  ''' XML mapping String ''' Conceptual-model name '''  Public Shared Function Parse(ByVal XMLString As String, ByVal ModelName As String) Return New MSLMappingAction(XElement.Parse(XMLString), ModelName) End Function '''  ''' Convert conceptual entity/property information into store table/column information '''  ''' Conceptual-model data ''' (.EntityName = entity expression String, .PropertyName = property expression String) ''' Store data (.TableName = table-name String, .ColumnName = column-name String) '''  Public Function ConceptualToStore(ByVal ConceptualInfo As MSLConceptualInfo) As MSLStoreInfo Dim StoreInfo As New MSLStoreInfo With ConceptualInfo ' prepare to query XML If Not .EntityName.Contains(".") Then ' make sure entity name is fully qualified .EntityName = mmaModelName & "." & .EntityName End If ' separate property names if there's complex-type nesting Dim Properties() As String = .PropertyName.Split(".") ' get relevant entity mapping Dim MappingInfo As IEnumerable(Of XElement) = _ (From mi In mmaMSLMapping.Descendants(FullElementName("EntityTypeMapping")) _ Where mi.@TypeName = "IsTypeOf(" & .EntityName & ")" _ OrElse mi.@TypeName = .EntityName _ Select mi) ' make sure entity is in model If IsSequenceEmpty(MappingInfo) Then Throw New EntityException("Entity """ & .EntityName & """ was not found!") End If ' get mapping fragments Dim MappingFragments As IEnumerable(Of XElement) = _ (From mf In MappingInfo.Descendants(FullElementName("MappingFragment")) _ Select mf) ' make sure there's at least 1 fragment If IsSequenceEmpty(MappingFragments) Then Throw New EntityException("Entity """ & .EntityName & """ was not mapped!") End If ' search each mapping fragment for the desired property For Each MappingFragment In MappingFragments ' get physical table for this fragment StoreInfo.TableName = MappingFragment.@StoreEntitySet ' search property expression chain Dim PropertyMapping As IEnumerable(Of XElement) = {MappingFragment} ' parse complex property info (if any) For index = 0 To UBound(Properties) - 1 ' go down 1 level Dim ComplexPropertyName = Properties(index) PropertyMapping = _ (From pm In PropertyMapping.Elements(FullElementName("ComplexProperty")) _ Where pm.@Name = ComplexPropertyName) ' verify that the property specified for this level exists If IsSequenceEmpty(PropertyMapping) Then Exit For 'go to next fragment if not End If Next index ' property not found? try next fragment If IsSequenceEmpty(PropertyMapping) Then Continue For End If ' parse scalar property info Dim ScalarPropertyName = Properties(UBound(Properties)) Dim ColumnName As String = _ (From pm In PropertyMapping.Elements(FullElementName("ScalarProperty")) _ Where pm.@Name = ScalarPropertyName _ Select CN = pm.@ColumnName).FirstOrDefault ' verify that scalar property exists If Not String.IsNullOrEmpty(ColumnName) Then ' yes? return (exit) with column info StoreInfo.ColumnName = ColumnName : Return StoreInfo End If Next MappingFragment ' property wasn't found Throw New EntityException("Property """ & .PropertyName _ & """ of entity """ & .EntityName & """ was not found!") End With End Function End Class '''  ''' Conceptual-model entity and property information '''  Public Structure MSLConceptualInfo '''  ''' Name of entity in conceptual model '''  ''' Entity expression String ''' EntityName may or may not be fully qualified (ie, "ModelName.EntityName"); ''' when a mapping method is called by the MSLMappingAction class, the conceptual model's ''' name and a period will be pre-pended if it's omitted Public Property EntityName As String '''  ''' Name of property in entity '''  ''' Property expression String ''' PropertyName may be either a stand-alone scalar property or a scalar property ''' within 1 or more levels of complex-type properties; in the latter case, it MUST be fully ''' qualified (ie, "ComplexPropertyName.InnerComplexPropertyName.ScalarPropertyName") Public Property PropertyName As String End Structure '''  ''' Database-store table and column information '''  Public Structure MSLStoreInfo '''  ''' Name of table in database '''  Public Property TableName As String '''  ''' Name of column in database table '''  Public Property ColumnName As String End Structure 

问题是节点名称都必须在它们前面加上一个名称空间。 它一直绊倒我,直到我一次检查我的元素1!

这是示例 XML – 我从上面的代码中的“。\ SCTModel.msl”文件加载:

                                                                                                               

我有点困惑为什么SQL Server中使用的原始表和列名称与模型的实体和属性名称不匹配。 除了用于提供多对多映射的表之外,(通常)应该是对象名称/属性与表名和列名之间的直接对应关系。

话虽如此,entity framework是一个ORM。 框架的整个目的是为数据库提供面向对象的视图,并抽象出必须直接与关系数据库交互。 EF并不意味着允许你绕过框架,据我所知,你不想做什么。 (但是,如果我错了,这是我今天学到的新东西,我会相应地删除或编辑这个答案。)