12
votes

in my project I use Entity Framework 6. I have these entities:

   public class Person
    {
        [Key]
        public int Id { get; set; }

        public string Name { get; set; }

        public virtual ICollection<PersonRight> PersonRights { get; set; }
    }

and

 public class PersonRight
    {
        [Key]
        public int Id { get; set; }

        public string Name { get; set; }
    }

When I insert a person object with filled in PersonRights it looks like this in the database:

table for Person entity:

dbo.People with columns Id, Name

table for PersonRights entity

dbo.PersonRights with columns Id, Name, Person_Id

when I load a person from a database it hasnt filled PersonRights property because of the virtual keyword which is enabeling the lazy loading feature - and its okay.

Then I get the PersonRights for the person object and it also works fine.

The thing is, since there is no navigation property in PersonRight entity, the entity framework must know by which columns in the database are those two properties bounded. In database ther is a foreign key connecting PersonRights and People tables:

FK_dbo.PersonRights_dbo.People_Person_Id

The question is : Is there any way how to get the column name by which are those two properties connected? Any way how to get the string "Person_Id" in code?

There is a way how to find out to which table is an entity bounded in database :

http://www.codeproject.com/Articles/350135/Entity-Framework-Get-mapped-table-name-from-an-ent

thanks a lot for your answers :)

EDIT:

Well I found out that the column name propety is here:

  var items = ((IObjectContextAdapter)dbContext).ObjectContext.MetadataWorkspace.GetItems(DataSpace.CSSpace);

but I still cant reach it, the problem is weird, when I get first item from this collection it shows me that its type is System.Data.Entity.Core.Mapping.StorageEntityContainerMapping but when I go through it by foreach suddenly the type is System.Data.Entity.Metadata.Edm.GlobalItem ...

How can I access the System.Data.Entity.Core.Mapping.StorageEntityContainerMapping item where is also the collection I need to get the column named - AssociationSetMappings ??

2
ok, I solved the table name problem ( codeproject.com/Articles/350135/…) but the foreign key column name problem is still unsolved.. - John Smith
Consider to post this solution as answer and accept it. It can be helpful for other people - Sergey Berezovskiy
Why doesn't PersonRight have property Person_Id? Is this code-first and you only add relationships in the database? - dudeNumber4
Yes, it is code first, and Im trying to avoid using navigation properties as much as possible so I did not include the Person_Id property in the PersonRight POCO class. But the mapping was made by entity framework itself and lies in the MetadataWorkspace. I just need to find a way how to reach these informations - John Smith
I don't know how replicate your situation; I can delete the navigation property from the model, but not Person_Id without causing errors (generated model from database). Which begs the question, why not just keep Person_Id in PersonRight but leave out the nav prop? - dudeNumber4

2 Answers

21
votes

You can get to the actual string "Person_Id" from the storage model, but you cannot identify that property/column as the foreign key. For that you would need Person_Id to exist in the conceptual model. I still don't quite understand why you wouldn't want it in the model, but here's how you would get it from the storage metadata:

using ( var context = new YourEntities() )
{
  var objectContext = ( ( IObjectContextAdapter )context ).ObjectContext;
  var storageMetadata = ( (EntityConnection)objectContext.Connection ).GetMetadataWorkspace().GetItems( DataSpace.SSpace );
  var entityProps = ( from s in storageMetadata where s.BuiltInTypeKind == BuiltInTypeKind.EntityType select s as EntityType );
  var personRightStorageMetadata = ( from m in entityProps where m.Name == "PersonRight" select m ).Single();
  foreach ( var item in personRightStorageMetadata.Properties )
  {
      Console.WriteLine( item.Name );
  }
}
1
votes

For EF6 I could only find the mappings in DataSpace.CSSpace (EntityTypeMapping will map entities to tables, and ScalarPropertyMapping will map scalar properties to columns):

using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;

// ...

using ( var db = new YourContext() )
{
  var metadataWorkspace = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)db)
  .ObjectContext.MetadataWorkspace;

  var itemCollection = ((StorageMappingItemCollection)metadataWorkspace
  .GetItemCollection(DataSpace.CSSpace));

  var entityMappings = itemCollection.OfType<EntityContainerMapping>().Single()
  .EntitySetMappings.ToList();

  var entityMapping = (EntityTypeMapping)entityMappings
    .Where(e => e.EntitySet.ElementType.FullName == typeof(TEntity).FullName)
    //or .Where(e => e.EntitySet.ElementType.Name == "YourEntityName")
    .Single().EntityTypeMappings.Single();

  var fragment = entityMapping.Fragments.Single();
  var dbTable = fragment.StoreEntitySet;

  Console.WriteLine($"Entity {entityMapping.EntityType.FullName} is mapped to table [{dbTable.Schema}].[{dbTable.Name}]");

  var scalarPropsMap = entityMapping.Fragments.Single()
  .PropertyMappings.OfType<ScalarPropertyMapping>();

  foreach(var prop in scalarPropsMap)
    Console.WriteLine($"Property {prop.Property.Name} maps to Column {prop.Column.Name}");

}

Out of curiosity I use the code above because System.Data.SqlClient.SqlBulkCopy requires mapping between entity properties and table columns.