locked
What's the best way to convert between entity/property names (conceptual) and table/field names (store)--using MSL mapping? RRS feed

  • Question

  • WHAT I HAVE:

    Visual Basic 2010, .NET 4.0, Entity Framework 4.0, SQL Express

    MY PROBLEM:

    Sometimes it's neccessary to convert between the names of the conceptual-model entities and properties and those of the store-model tables and fields that they correspond to--for instance, if one wants to create a secondary index on an entity's table (using T-SQL and CONSTRAINT).

    I have figured (by looking at the XML of the metadata files) that the best way to parse this mapping is to use the MSL layer through the MetadataWorkspace's CSSpace (not CSpace), but I have a problem figuring out what the MW code to properly query it would be; I'm new to using the workspace (as well as to reflection).

    I need methods to do the following:

    1. Convert any entity to a String representing the name of its table. The entity should be supplied using either its object (good) or a String for its name (better). (NOTE: My model does not do "multiple-table mappings" for any single entity, so the code doesn't absolutely have to account for that.)
    2. Convert any property in an entity to its table field; once again, from (conceptual) object/name-String to a (store) name-String. (It would be really nice to specify the property as a name-String, so I can imbed the indexing info in a resource file.) The code must account for the possibility that the property for which the field is sought may be nested inside a property represented by an EF (user-defined) "complex type".
    3. Consist of source code which a) can be re-used without change in other projects with different models (that is, it should be "general-purpose"), and b) is in Visual Basic if it all possible, as my project is being written in it. (I do have a C-to-VB converter, but I prefer to use it only when necessary, as such tools are imperfect.)

    Any solutions?


    Robert Gustafson

















    Sunday, July 6, 2014 3:42 AM

Answers

  • About this XML file, an app.config is just another file XML too. So you could put this XML into an app.config. You could encrypt the app.config. When the exe project is built, a runtime config called programname.exe.config is created, which is at the same location of the programname.exe is located. It's in your bin folder right there with the exe file. The runtime.config must be deployed with the exe in the same location as the exe so .NET can find it.

    So you can maybe put your XML in the app.config/progrmname.exe.config and access your file that way.

    There are articles on Bing or Google on how to encrypt  and decrypt the app.config.

     
    • Marked as answer by Fred Bao Monday, July 21, 2014 8:40 AM
    Sunday, July 13, 2014 9:18 AM
  • I have written a new routine that to convert an entity/property pair into a table/column pair. This class, MSLMappingAction, takes in its constructor the model name and either an XElement XML tree, an MSL mapping file, or an XML string. One then uses the ConceptualToStore method to take String specifying entity and property "expressions" (stored in the MSLConceptualInfo structure) and find the table and column names (stored in the MSLStoreInfo structure).

    Notes:

    1. One could also write a "StoreToConceptual" method to convert in the other direction, but the Linq-to-XML queries would probably be a bit more complex. The same goes for handling mapping of navigation properties, functions, and stored procedures.
    2. Be careful when working with properties inherited by derived entities! If a property isn't specific to the derived entity, then you should use the name of the base entity.

    Here's the sample XML -- which I load from the ".\SCTModel.msl" file in the code (see next post):

    <?xml version="1.0" encoding="utf-8"?>
    <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs">
      <EntityContainerMapping StorageEntityContainer="SCTModelStoreContainer" CdmEntityContainer="SocialContactsTracker">
        <EntitySetMapping Name="SocialContacts">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.SocialContact)">
            <MappingFragment StoreEntitySet="SocialContacts">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DateAdded" ColumnName="DateAdded" />
              <ScalarProperty Name="Information" ColumnName="Information" />
              <ComplexProperty Name="DefaultAssociations" TypeName="SCTModel.DefaultAssociations">
                <ScalarProperty Name="DefaultLocationID" ColumnName="DefaultAssociations_DefaultLocationID" />
                <ScalarProperty Name="DefaultEmailID" ColumnName="DefaultAssociations_DefaultEmailID" />
                <ScalarProperty Name="DefaultPhoneNumberID" ColumnName="DefaultAssociations_DefaultPhoneNumberID" />
                <ScalarProperty Name="DefaultWebsiteID" ColumnName="DefaultAssociations_DefaultWebsiteID" />
              </ComplexProperty>
              <ScalarProperty Name="Picture" ColumnName="Picture" />
            </MappingFragment>
          </EntityTypeMapping>
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Person)">
            <MappingFragment StoreEntitySet="SocialContacts_Person">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DateOfBirth" ColumnName="DateOfBirth" />
              <ScalarProperty Name="FirstName" ColumnName="FirstName" />
              <ScalarProperty Name="LastName" ColumnName="LastName" />
            </MappingFragment>
          </EntityTypeMapping>
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Organization)">
            <MappingFragment StoreEntitySet="SocialContacts_Organization">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="Name" ColumnName="Name" />
              <ScalarProperty Name="DateOfCreation" ColumnName="DateOfCreation" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Locations">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Location)">
            <MappingFragment StoreEntitySet="Locations">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="City" ColumnName="City" />
              <ScalarProperty Name="State" ColumnName="State" />
              <ScalarProperty Name="ZIP" ColumnName="ZIP" />
              <ScalarProperty Name="Country" ColumnName="Country" />
              <ComplexProperty Name="Address" TypeName="SCTModel.Address">
                <ScalarProperty Name="Street" ColumnName="Address_Street" />
                <ScalarProperty Name="Apartment" ColumnName="Address_Apartment" />
                <ScalarProperty Name="HouseNumber" ColumnName="Address_HouseNumber" />
              </ComplexProperty>
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="PhoneNumbers">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.PhoneNumber)">
            <MappingFragment StoreEntitySet="PhoneNumbers">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="Number" ColumnName="Number" />
              <ScalarProperty Name="PhoneType" ColumnName="PhoneType" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Emails">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Email)">
            <MappingFragment StoreEntitySet="Emails">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DomainName" ColumnName="DomainName" />
              <ScalarProperty Name="UserName" ColumnName="UserName" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Websites">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Website)">
            <MappingFragment StoreEntitySet="Websites">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="URL" ColumnName="URL" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <AssociationSetMapping Name="SocialContactWebsite" TypeName="SCTModel.SocialContactWebsite" StoreEntitySet="SocialContactWebsite">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Website">
            <ScalarProperty Name="Id" ColumnName="Websites_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactPhoneNumber" TypeName="SCTModel.SocialContactPhoneNumber" StoreEntitySet="SocialContactPhoneNumber">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="PhoneNumber">
            <ScalarProperty Name="Id" ColumnName="PhoneNumbers_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactEmail" TypeName="SCTModel.SocialContactEmail" StoreEntitySet="SocialContactEmail">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Email">
            <ScalarProperty Name="Id" ColumnName="Emails_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactLocation" TypeName="SCTModel.SocialContactLocation" StoreEntitySet="SocialContactLocation">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Location">
            <ScalarProperty Name="Id" ColumnName="Locations_Id" />
          </EndProperty>
        </AssociationSetMapping>
      </EntityContainerMapping>
    </Mapping>

    The code follows below


    Robert Gustafson

    Thursday, July 31, 2014 7:15 AM

  • And here's the code (see previous post for sample XML):

    Host code (It returns table name "Locations" and column name "Address_Street" for the store information--when given the model name "SCTModel", the entity "Location", and the property expression "Address.Street"):

    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 code:

    Option Infer On
    Imports System.Xml.Linq
     
    ''' <summary>
    ''' This class allows one to convert between an EF conceptual model's entity/property pair
    ''' and its database store's table/column pair.
    ''' </summary>
    ''' <remarks>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)</remarks>
    Public Class MSLMappingAction
     
    '   private fields and routines
    Private mmaMSLMapping As XElement
    Private mmaModelNamemmaNamespace As String
     
    Private Function FullElementName(ByVal ElementName As StringAs String
    '   pre-pend Namespace to ElementName
    Return "{" & mmaNamespace & "}" & ElementName
    End Function
     
    Private Sub ValidateParams(ByVal MappingXML As XElementByval ModelName As String)
    '   verify that model name is specified
    If String.IsNullOrEmpty(ModelNameThen
    	Throw New EntityException("Entity model name is not given!")
    End If
    '   verify that we're using C-S space
    If MappingXML.@Space <> "C-S" Then
    	Throw New MetadataException("XML is not C-S 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
    ''' <summary>
    ''' Name of conceptual entity model
    ''' </summary>
    ''' <returns>Conceptual-model String</returns>
    ''' <remarks>Model name can only be set in constructor</remarks>
    Public ReadOnly Property EntityModelName() As String
    Get
    	Return mmaModelName
    End Get
    End Property
     
    ''' <summary>
    ''' Name of mapping namespace
    ''' </summary>
    ''' <returns>Namespace String of C-S mapping layer</returns>
    ''' <remarks>This value is determined when the XML mapping
    ''' is first parsed in the constructor</remarks>
    Public ReadOnly Property MappingNamespace() As String
    Get
    	Return mmaNamespace
    End Get
    End Property
     
    '   constructors
    ''' <summary>
    ''' Get C-S mapping information for an entity model (with XML tree)
    ''' </summary>
    ''' <param name="MappingXML">XML mapping tree</param>
    ''' <param name="ModelName">Conceptual-model name</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal MappingXML As XElementByVal ModelName As String)
    ValidateParams(MappingXMLModelName)
    End Sub
     
    ''' <summary>
    ''' Get C-S mapping information for an entity model (with XML file)
    ''' </summary>
    ''' <param name="MSLFile">MSL mapping file</param>
    ''' <param name="ModelName">Conceptual-model name</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal MSLFile As StringByVal ModelName As String)
    Dim MappingXML As XElement = XElement.Load(MSLFile)
    ValidateParams(MappingXMLModelName)
    End Sub
     
    '   methods
    ''' <summary>
    ''' Get C-S mapping infomration for an entity model (with XML String)
    ''' </summary>
    ''' <param name="XMLString">XML mapping String</param>
    ''' <param name="ModelName">Conceptual-model name</param>
    ''' <returns></returns>
    Public Shared Function Parse(ByVal XMLString As StringByVal ModelName As String)
    Return New MSLMappingAction(XElement.Parse(XMLString), ModelName)
    End Function
     
    ''' <summary>
    ''' Convert conceptual entity/property information into store table/column information
    ''' </summary>
    ''' <param name="ConceptualInfo">Conceptual-model data
    ''' (.EntityName = entity expression String, .PropertyName = property expression String)</param>
    ''' <returns>Store data (.TableName = table-name String, .ColumnName = column-name String)</returns>
    ''' <remarks></remarks>
    Public Function ConceptualToStore(ByVal ConceptualInfo As MSLConceptualInfoAs 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(MappingInfoThen
    		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(MappingFragmentsThen
    		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(PropertyMappingThen
    				Exit For 'go to next fragment if not
    			End If
    		Next index
    		'   property not found? try next fragment
    		If IsSequenceEmpty(PropertyMappingThen
    			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(ColumnNameThen
    			'   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
     
    ''' <summary>
    ''' Conceptual-model entity and property information  
    ''' </summary>
    Public Structure MSLConceptualInfo
    ''' <summary>
    ''' Name of entity in conceptual model
    ''' </summary>
    ''' <value>Entity expression String</value>
    ''' <remarks>EntityName may or may not be fully qualified (i.e., "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</remarks>
    Public Property EntityName As String
    ''' <summary>
    ''' Name of property in entity
    ''' </summary>
    ''' <value>Property expression String</value>
    ''' <remarks>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 (i.e., "ComplexPropertyName.InnerComplexPropertyName.ScalarPropertyName")</remarks>
    Public Property PropertyName As String
    End Structure
     
    ''' <summary>
    ''' Database-store table and column information
    ''' </summary>
    Public Structure MSLStoreInfo
    ''' <summary>
    ''' Name of table in database
    ''' </summary>
    Public Property TableName As String
    ''' <summary>
    ''' Name of column in database table
    ''' </summary>
    Public Property ColumnName As String
    End Structure
     

    The catch is that the node names all have to have a namespace prepended to them. It tripped me up until I checked my XML elements 1 at a time!


    Robert Gustafson




    Thursday, July 31, 2014 7:15 AM

All replies

  • Hello,

    >>What's the best way to convert between entity/property names (conceptual) and table/field names (store)--using MSL mapping?

    I am a bit confused your question, is it thatyou are trying to generate your own entity class? If it is,since these entities are generated by T4 template, you needto modify it to fit your meet as:

    http://blogs.msdn.com/b/efdesign/archive/2009/01/22/customizing-entity-classes-with-t4.aspx

    If I misunderstand, please let me know.

    Regards.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Monday, July 7, 2014 3:37 AM
  • There are so many things that EF can do and so many things people want to do with it that confusion on questions like mine are inevatable. Let me try again: I am NOT trying to create a "new" entity class. I am trying to, given an existing entity class and its properties, retrieve (programmatically) the table/field names so I can create additional indexes on the tables the entities are mapped to. The idea is resuable methods that do, for any arbitrary project & its constructed EF model, the following:

    For an (existing) entity, take the object or its name's String, and get its table's name; and

    for an (existing) entity's property (beit a scalar or a part of a complex-type property), take its object or name's String, and get the table column name.

    The idea is to have a project able to convert its conceptual setup information into the corresponding store info (at run-time) so the project can do some things that require using the underlying tables and fields, like creating additional CONSTRAINTs on the model's database. I want to use the methods of the MetadataWorkspace's "C-S space" (DataSpace.CSSpace), with its mapping info, to query the metadata needed to do the conversion. I'D DO THIS MYSELF EXCEPT THAT I'M NEW, EF-WISE, TO USING THE WORKSPACE'S MEMBERS AND I WANT A QUICK SOLUTION (or at least something to get me started)--and I've found little documentation, let alone examples, on how to query the "mapping layer" info programmatically.

     

    PS. My project also creates at run-time the physical database instance (catalog/file, using ObjectContext.CreateDatabase); but that's not a relavent factor to the problem at hand. (The point is I like to do some of the work programmatically.)

    PPS. I'm using TPT inheritance with my entities, and mapping the entities to single tables and vice versa (as opposed to using TPH and breaking entities between tables or vice versa), so somethng tailored to that specific reality will do--although I don't mind something more general-purpose.

    Robert Gustafson
















    Monday, July 7, 2014 3:59 AM
  • Man, you need some kind of standalone solution that is going to this. The solution has to be able to read the edmx file and get the information needed. The edmx file is just a XML file, and you can use Linq-2-XML to read the edmx file.

    This program has to be able to read a SQL Server table scheam too and get the information about a table's column, which I have seen done with an in-house written object code gnerator that read table scheams and generated DTO(s) Data Trnasfer Objects/classes based on a table's column definitions.

    Combining the two things above into a standalone solution may be a way to go for you.   

    Monday, July 7, 2014 8:00 PM
  • I don't think I need to parse the edmx file, or a standalone. The XML of the MSL metadata file ("C-S Space") contains the information I need (I've looked at it). What I don't know is how to query CSSpace layer. Since the MetadataWorkspace provides for DataSpace.CSSpace, I should be able to query the mapping layer with the workspace methods, although I could use Linq-to-XML on the MSL file.

    I'm not too experienced with querying the workspace or XML, however. All examples I've seen of workspace-querying code deal with SSpace or CSpace--the conceptual and store layers by themselves--as opposed CSSpace--which handles the mapping between them.

    Here is the MSL file I've got:

    <?xml version="1.0" encoding="utf-8"?>
    <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs">
      <EntityContainerMapping StorageEntityContainer="SCTModelStoreContainer" CdmEntityContainer="SocialContactsTracker">
        <EntitySetMapping Name="SocialContacts">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.SocialContact)">
            <MappingFragment StoreEntitySet="SocialContacts">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DateAdded" ColumnName="DateAdded" />
              <ScalarProperty Name="Information" ColumnName="Information" />
              <ComplexProperty Name="DefaultAssociations" TypeName="SCTModel.DefaultAssociations">
                <ScalarProperty Name="DefaultLocationID" ColumnName="DefaultAssociations_DefaultLocationID" />
                <ScalarProperty Name="DefaultEmailID" ColumnName="DefaultAssociations_DefaultEmailID" />
                <ScalarProperty Name="DefaultPhoneNumberID" ColumnName="DefaultAssociations_DefaultPhoneNumberID" />
                <ScalarProperty Name="DefaultWebsiteID" ColumnName="DefaultAssociations_DefaultWebsiteID" />
              </ComplexProperty>
              <ScalarProperty Name="Picture" ColumnName="Picture" />
            </MappingFragment>
          </EntityTypeMapping>
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Person)">
            <MappingFragment StoreEntitySet="SocialContacts_Person">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DateOfBirth" ColumnName="DateOfBirth" />
              <ScalarProperty Name="FirstName" ColumnName="FirstName" />
              <ScalarProperty Name="LastName" ColumnName="LastName" />
            </MappingFragment>
          </EntityTypeMapping>
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Organization)">
            <MappingFragment StoreEntitySet="SocialContacts_Organization">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="Name" ColumnName="Name" />
              <ScalarProperty Name="DateOfCreation" ColumnName="DateOfCreation" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Locations">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Location)">
            <MappingFragment StoreEntitySet="Locations">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="City" ColumnName="City" />
              <ScalarProperty Name="State" ColumnName="State" />
              <ScalarProperty Name="ZIP" ColumnName="ZIP" />
              <ScalarProperty Name="Country" ColumnName="Country" />
              <ComplexProperty Name="Address" TypeName="SCTModel.Address">
                <ScalarProperty Name="Street" ColumnName="Address_Street" />
                <ScalarProperty Name="Apartment" ColumnName="Address_Apartment" />
                <ScalarProperty Name="HouseNumber" ColumnName="Address_HouseNumber" />
              </ComplexProperty>
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="PhoneNumbers">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.PhoneNumber)">
            <MappingFragment StoreEntitySet="PhoneNumbers">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="Number" ColumnName="Number" />
              <ScalarProperty Name="PhoneType" ColumnName="PhoneType" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Emails">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Email)">
            <MappingFragment StoreEntitySet="Emails">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DomainName" ColumnName="DomainName" />
              <ScalarProperty Name="UserName" ColumnName="UserName" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Websites">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Website)">
            <MappingFragment StoreEntitySet="Websites">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="URL" ColumnName="URL" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <AssociationSetMapping Name="SocialContactWebsite" TypeName="SCTModel.SocialContactWebsite" StoreEntitySet="SocialContactWebsite">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Website">
            <ScalarProperty Name="Id" ColumnName="Websites_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactPhoneNumber" TypeName="SCTModel.SocialContactPhoneNumber" StoreEntitySet="SocialContactPhoneNumber">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="PhoneNumber">
            <ScalarProperty Name="Id" ColumnName="PhoneNumbers_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactEmail" TypeName="SCTModel.SocialContactEmail" StoreEntitySet="SocialContactEmail">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Email">
            <ScalarProperty Name="Id" ColumnName="Emails_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactLocation" TypeName="SCTModel.SocialContactLocation" StoreEntitySet="SocialContactLocation">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Location">
            <ScalarProperty Name="Id" ColumnName="Locations_Id" />
          </EndProperty>
        </AssociationSetMapping>
      </EntityContainerMapping>
    </Mapping>

    So how does my program query this--using the workspace methods for DataSpace.CSSpace or Linq-to-XML--in order to find that, for instance, the following?

    Entity "Person" corresponds to table "SocialContacts_Person";

    Within "Person", property "FirstName" corresponds to field "FirstName";

    Entity "Location" corresponds to table "Locations";

    Within "Location", property "State" of Location corresponds to field "State", and

    property "Address.Street"(complex property) corresponds to field "Address_Street"

    I know the relationships above, but the program would have to query the mapping layer to figure this out. I could just use the store info, but I figure that my program's more flexible in case of future changes if it uses the entity info and figures out the store info.

    I've noticed in the XML above that the mapping XML data I need is nested within several layers, and most workspace-query and XML-query examples I've seen don't go very deep. I need either the best specific solution for parsing an arbitrary model's mapping layer or something to get me started that I can develop further.

    PS. I'm not looking for a "code-generation" thing. I want the mapping from conceptual to store info probed by the program at run-time.

    Something like this:

    Function GetTableName(EntityName As String) As String

    Function GetColumnName(EntityName As String, PropertyName As String) As String

    (with PropertyName either a scalar property like "FirstName" or a complex property like "Address.Street") 


    Robert Gustafson







    Wednesday, July 9, 2014 6:33 AM
  • It's just XML man load it into a XML Document and query it, which would result in you doing a Linq projection to create an anonymous object or a List<T> of anonymous objects that you can access the results in the form of objects 

    http://data.bangtech.com/vb/linq2xml.htm

    http://msdn.microsoft.com/en-us/library/bb397989(v=vs.90).aspx

    There are plenty of examples of how to use Linq-2-XML and do queries against XML - use Bing or Google, and you may need to look at some c# examples just to see what is happening.

    Wednesday, July 9, 2014 11:18 AM
  • Thank you, I'll use that approach; the help file provides several examples on how to query XML. One question, though: What folder is the mapping file supposed to be in? Under the current setup, the program's connection string looks in "res://*/" (resource-embedded) for the files, although the files are also saved in the application folder. Can I use "res://*/" when loading the XML, or should I get it from the app folder?

    Robert Gustafson



    Thursday, July 10, 2014 9:22 PM
  • Thank you, I'll use that approach; the help file provides several examples on how to query XML.One question, though: What folder is the mapping file supposed to be in? Under the current setup, the program's connection string looks in "res://*/" (resource-embedded) for the files, although the files are also saved in the application folder. Can I use "res://*/" when loading the XML, or should I get it from the app folder?

    I would just put some kind of pathing in a config file and use it. If you know where the file is going to be at in a permanent location, then you should just map a path to it. Do you need to get fancy with it?

    Friday, July 11, 2014 2:04 PM
  • My App.Config file uses "res://*/MSLfile.msl" for the MSL file. Can I specify "res://*/MSLfile.msl" as a path for a VB XML Load method? All I need is an answer to this question and I can get started!

    Robert Gustafson


    Saturday, July 12, 2014 9:04 PM
  • XMLDocument.Load("path") hey try your way and see if it will path the way you want it. That's the best I can tell you.
    Saturday, July 12, 2014 9:36 PM
  • I did try. The Load method does not recognise "res://" as a path starter. To get to the file I had to do the following:

    Dim X As XElement = XElement.Load("..\..\obj\x86\Debug\edmxResourcesToEmbed\SCTModel.msl")
    

    I don't think this will work in the distrubuted EXE version, even if I change "Debug" to "Release", as the resource files will simply be folded into the EXE (will they?). It leaves me either having to 1) find a way to access a file that's embedded as a resource (how?), 2) make a copy of the MSL and access that (would have to be updated with any change to the model), or 3) use the MetadataWorkspace methods using DataSpace.CSSpace (once again, no examples). A solution seems so close, yet so far away.



    Robert Gustafson


    Saturday, July 12, 2014 10:28 PM
  • UPDATE:

    I looked at some other people's questions on matters closely related to this one, and apparantly approach #3--accessing the mapping layer using the MetadataWorkspace object--just isn't available, even though the enumeration constant DataSpace.CSSpace is available (Why did Microsoft include the constant but not the support?). Therefore, I have to parse XML from either the program resources (How do I do that?) or the MSL file. If I take the latter approach (have a copy of the MSL file available separately), is there a way to keep the MSL file from being parsed outside of my application? (I wouldn't want to expose the mapping info to the entire world when my app is distributed.) 


    Robert Gustafson

    Saturday, July 12, 2014 11:18 PM
  • About this XML file, an app.config is just another file XML too. So you could put this XML into an app.config. You could encrypt the app.config. When the exe project is built, a runtime config called programname.exe.config is created, which is at the same location of the programname.exe is located. It's in your bin folder right there with the exe file. The runtime.config must be deployed with the exe in the same location as the exe so .NET can find it.

    So you can maybe put your XML in the app.config/progrmname.exe.config and access your file that way.

    There are articles on Bing or Google on how to encrypt  and decrypt the app.config.

     
    • Marked as answer by Fred Bao Monday, July 21, 2014 8:40 AM
    Sunday, July 13, 2014 9:18 AM
  • Thank you for your ideas. BTW, if the program stores the metadata files as resources, how do I extract them--in particular, what assembly would my program have to look in?


    Robert Gustafson

    Monday, July 14, 2014 11:13 PM
  • I don't know. You'll have to use Bing or Google to find something.
    Tuesday, July 15, 2014 1:13 PM
  • I have written a new routine that to convert an entity/property pair into a table/column pair. This class, MSLMappingAction, takes in its constructor the model name and either an XElement XML tree, an MSL mapping file, or an XML string. One then uses the ConceptualToStore method to take String specifying entity and property "expressions" (stored in the MSLConceptualInfo structure) and find the table and column names (stored in the MSLStoreInfo structure).

    Notes:

    1. One could also write a "StoreToConceptual" method to convert in the other direction, but the Linq-to-XML queries would probably be a bit more complex. The same goes for handling mapping of navigation properties, functions, and stored procedures.
    2. Be careful when working with properties inherited by derived entities! If a property isn't specific to the derived entity, then you should use the name of the base entity.

    Here's the sample XML -- which I load from the ".\SCTModel.msl" file in the code (see next post):

    <?xml version="1.0" encoding="utf-8"?>
    <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs">
      <EntityContainerMapping StorageEntityContainer="SCTModelStoreContainer" CdmEntityContainer="SocialContactsTracker">
        <EntitySetMapping Name="SocialContacts">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.SocialContact)">
            <MappingFragment StoreEntitySet="SocialContacts">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DateAdded" ColumnName="DateAdded" />
              <ScalarProperty Name="Information" ColumnName="Information" />
              <ComplexProperty Name="DefaultAssociations" TypeName="SCTModel.DefaultAssociations">
                <ScalarProperty Name="DefaultLocationID" ColumnName="DefaultAssociations_DefaultLocationID" />
                <ScalarProperty Name="DefaultEmailID" ColumnName="DefaultAssociations_DefaultEmailID" />
                <ScalarProperty Name="DefaultPhoneNumberID" ColumnName="DefaultAssociations_DefaultPhoneNumberID" />
                <ScalarProperty Name="DefaultWebsiteID" ColumnName="DefaultAssociations_DefaultWebsiteID" />
              </ComplexProperty>
              <ScalarProperty Name="Picture" ColumnName="Picture" />
            </MappingFragment>
          </EntityTypeMapping>
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Person)">
            <MappingFragment StoreEntitySet="SocialContacts_Person">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DateOfBirth" ColumnName="DateOfBirth" />
              <ScalarProperty Name="FirstName" ColumnName="FirstName" />
              <ScalarProperty Name="LastName" ColumnName="LastName" />
            </MappingFragment>
          </EntityTypeMapping>
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Organization)">
            <MappingFragment StoreEntitySet="SocialContacts_Organization">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="Name" ColumnName="Name" />
              <ScalarProperty Name="DateOfCreation" ColumnName="DateOfCreation" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Locations">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Location)">
            <MappingFragment StoreEntitySet="Locations">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="City" ColumnName="City" />
              <ScalarProperty Name="State" ColumnName="State" />
              <ScalarProperty Name="ZIP" ColumnName="ZIP" />
              <ScalarProperty Name="Country" ColumnName="Country" />
              <ComplexProperty Name="Address" TypeName="SCTModel.Address">
                <ScalarProperty Name="Street" ColumnName="Address_Street" />
                <ScalarProperty Name="Apartment" ColumnName="Address_Apartment" />
                <ScalarProperty Name="HouseNumber" ColumnName="Address_HouseNumber" />
              </ComplexProperty>
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="PhoneNumbers">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.PhoneNumber)">
            <MappingFragment StoreEntitySet="PhoneNumbers">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="Number" ColumnName="Number" />
              <ScalarProperty Name="PhoneType" ColumnName="PhoneType" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Emails">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Email)">
            <MappingFragment StoreEntitySet="Emails">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="DomainName" ColumnName="DomainName" />
              <ScalarProperty Name="UserName" ColumnName="UserName" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <EntitySetMapping Name="Websites">
          <EntityTypeMapping TypeName="IsTypeOf(SCTModel.Website)">
            <MappingFragment StoreEntitySet="Websites">
              <ScalarProperty Name="Id" ColumnName="Id" />
              <ScalarProperty Name="URL" ColumnName="URL" />
            </MappingFragment>
          </EntityTypeMapping>
        </EntitySetMapping>
        <AssociationSetMapping Name="SocialContactWebsite" TypeName="SCTModel.SocialContactWebsite" StoreEntitySet="SocialContactWebsite">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Website">
            <ScalarProperty Name="Id" ColumnName="Websites_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactPhoneNumber" TypeName="SCTModel.SocialContactPhoneNumber" StoreEntitySet="SocialContactPhoneNumber">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="PhoneNumber">
            <ScalarProperty Name="Id" ColumnName="PhoneNumbers_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactEmail" TypeName="SCTModel.SocialContactEmail" StoreEntitySet="SocialContactEmail">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Email">
            <ScalarProperty Name="Id" ColumnName="Emails_Id" />
          </EndProperty>
        </AssociationSetMapping>
        <AssociationSetMapping Name="SocialContactLocation" TypeName="SCTModel.SocialContactLocation" StoreEntitySet="SocialContactLocation">
          <EndProperty Name="SocialContact">
            <ScalarProperty Name="Id" ColumnName="SocialContacts_Id" />
          </EndProperty>
          <EndProperty Name="Location">
            <ScalarProperty Name="Id" ColumnName="Locations_Id" />
          </EndProperty>
        </AssociationSetMapping>
      </EntityContainerMapping>
    </Mapping>

    The code follows below


    Robert Gustafson

    Thursday, July 31, 2014 7:15 AM

  • And here's the code (see previous post for sample XML):

    Host code (It returns table name "Locations" and column name "Address_Street" for the store information--when given the model name "SCTModel", the entity "Location", and the property expression "Address.Street"):

    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 code:

    Option Infer On
    Imports System.Xml.Linq
     
    ''' <summary>
    ''' This class allows one to convert between an EF conceptual model's entity/property pair
    ''' and its database store's table/column pair.
    ''' </summary>
    ''' <remarks>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)</remarks>
    Public Class MSLMappingAction
     
    '   private fields and routines
    Private mmaMSLMapping As XElement
    Private mmaModelNamemmaNamespace As String
     
    Private Function FullElementName(ByVal ElementName As StringAs String
    '   pre-pend Namespace to ElementName
    Return "{" & mmaNamespace & "}" & ElementName
    End Function
     
    Private Sub ValidateParams(ByVal MappingXML As XElementByval ModelName As String)
    '   verify that model name is specified
    If String.IsNullOrEmpty(ModelNameThen
    	Throw New EntityException("Entity model name is not given!")
    End If
    '   verify that we're using C-S space
    If MappingXML.@Space <> "C-S" Then
    	Throw New MetadataException("XML is not C-S 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
    ''' <summary>
    ''' Name of conceptual entity model
    ''' </summary>
    ''' <returns>Conceptual-model String</returns>
    ''' <remarks>Model name can only be set in constructor</remarks>
    Public ReadOnly Property EntityModelName() As String
    Get
    	Return mmaModelName
    End Get
    End Property
     
    ''' <summary>
    ''' Name of mapping namespace
    ''' </summary>
    ''' <returns>Namespace String of C-S mapping layer</returns>
    ''' <remarks>This value is determined when the XML mapping
    ''' is first parsed in the constructor</remarks>
    Public ReadOnly Property MappingNamespace() As String
    Get
    	Return mmaNamespace
    End Get
    End Property
     
    '   constructors
    ''' <summary>
    ''' Get C-S mapping information for an entity model (with XML tree)
    ''' </summary>
    ''' <param name="MappingXML">XML mapping tree</param>
    ''' <param name="ModelName">Conceptual-model name</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal MappingXML As XElementByVal ModelName As String)
    ValidateParams(MappingXMLModelName)
    End Sub
     
    ''' <summary>
    ''' Get C-S mapping information for an entity model (with XML file)
    ''' </summary>
    ''' <param name="MSLFile">MSL mapping file</param>
    ''' <param name="ModelName">Conceptual-model name</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal MSLFile As StringByVal ModelName As String)
    Dim MappingXML As XElement = XElement.Load(MSLFile)
    ValidateParams(MappingXMLModelName)
    End Sub
     
    '   methods
    ''' <summary>
    ''' Get C-S mapping infomration for an entity model (with XML String)
    ''' </summary>
    ''' <param name="XMLString">XML mapping String</param>
    ''' <param name="ModelName">Conceptual-model name</param>
    ''' <returns></returns>
    Public Shared Function Parse(ByVal XMLString As StringByVal ModelName As String)
    Return New MSLMappingAction(XElement.Parse(XMLString), ModelName)
    End Function
     
    ''' <summary>
    ''' Convert conceptual entity/property information into store table/column information
    ''' </summary>
    ''' <param name="ConceptualInfo">Conceptual-model data
    ''' (.EntityName = entity expression String, .PropertyName = property expression String)</param>
    ''' <returns>Store data (.TableName = table-name String, .ColumnName = column-name String)</returns>
    ''' <remarks></remarks>
    Public Function ConceptualToStore(ByVal ConceptualInfo As MSLConceptualInfoAs 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(MappingInfoThen
    		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(MappingFragmentsThen
    		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(PropertyMappingThen
    				Exit For 'go to next fragment if not
    			End If
    		Next index
    		'   property not found? try next fragment
    		If IsSequenceEmpty(PropertyMappingThen
    			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(ColumnNameThen
    			'   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
     
    ''' <summary>
    ''' Conceptual-model entity and property information  
    ''' </summary>
    Public Structure MSLConceptualInfo
    ''' <summary>
    ''' Name of entity in conceptual model
    ''' </summary>
    ''' <value>Entity expression String</value>
    ''' <remarks>EntityName may or may not be fully qualified (i.e., "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</remarks>
    Public Property EntityName As String
    ''' <summary>
    ''' Name of property in entity
    ''' </summary>
    ''' <value>Property expression String</value>
    ''' <remarks>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 (i.e., "ComplexPropertyName.InnerComplexPropertyName.ScalarPropertyName")</remarks>
    Public Property PropertyName As String
    End Structure
     
    ''' <summary>
    ''' Database-store table and column information
    ''' </summary>
    Public Structure MSLStoreInfo
    ''' <summary>
    ''' Name of table in database
    ''' </summary>
    Public Property TableName As String
    ''' <summary>
    ''' Name of column in database table
    ''' </summary>
    Public Property ColumnName As String
    End Structure
     

    The catch is that the node names all have to have a namespace prepended to them. It tripped me up until I checked my XML elements 1 at a time!


    Robert Gustafson




    Thursday, July 31, 2014 7:15 AM