2013-03-30 9 views
16

Ho un'applicazione che utilizza EF-CodeFirst 5 (dll ver 4.4.0.0, su .net 4.0).Come posso leggere i metadati EF DbContext a livello di codice?

ho bisogno di essere in grado di leggere i metadati entità, in modo che possa, per un determinato tipo di voce ottenere le seguenti informazioni:

  • quali proprietà sono uno-molti rapporti (entità cui si fa riferimento)
  • quali proprietà sono molti-uno rapporti (collezioni di entità fanno riferimento a quello attuale)
  • anche bello, ma non è assolutamente necessario: quali proprietà sono molti-molti rapporti (raccolte di relazioni)

Posso ottenere queste informazioni scrivendo foreach loop su liste di proprietà e quindi "riconoscendole" facendo affidamento su tutti i riferimenti che sono virtuali, ma ritengo che non sia un modo "corretto". So che EdmxWriter può fornire quell'informazione in formato xml, ma lo fa accedendo a InternalContext che non è pubblicamente accessibile e voglio ottenere direttamente elenchi/array fortemente digitati, senza usare quel xml. Quale API dovrei usare (se ce n'è una per questo, sembra che non riesca a trovarla)?

+1

ora ci sono piani per migliorare l'API Metadata: https://entityframework.codeplex.com/workitem/1471 Maggiori informazioni qui: http://romiller.com/2013/09/24/ef-code- first-mapping-between-types-tables/ – Colin

risposta

25

Gorane, questo dovrebbe iniziare ...
(non ho giocato molto con esso - ci vuole un po 'di sperimentazione nel debugger per vedere quali proprietà/informazioni e come ottenerlo)

using (var db = new MyDbContext()) 
{ 
    var objectContext = ((IObjectContextAdapter)db).ObjectContext; 
    var container = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace); 
    foreach (var set in container.BaseEntitySets) 
    { 
     // set.ElementType. 
     foreach (var metaproperty in set.MetadataProperties) 
     { 
      // metaproperty. 
     } 
    } 

    // ...or... 

    var keyName = objectContext 
     .MetadataWorkspace 
     .GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace) 
     .BaseEntitySets 
     .First(meta => meta.ElementType.Name == "Question") 
     .ElementType 
     .KeyMembers 
     .Select(k => k.Name) 
     .FirstOrDefault(); 
} 

e più precisamente ...

foreach (var set in container.BaseEntitySets) 
{ 
    var dependents = ((EntitySet)(set)).ForeignKeyDependents; 
    var principals = ((EntitySet)(set)).ForeignKeyPrincipals; 
    var navigationProperties = ((EntityType)(set.ElementType)).NavigationProperties; 
    foreach (var nav in navigationProperties) 
    { 
     // nav.RelationshipType; 
    } 
} 

alcune di queste proprietà sembrano non essere esposti a 'pubblico' così avresti bisogno di utilizzare la riflessione - o trovare un modo più intelligente - ma un buon affare di informazioni è lì dentro



E qualche informazione in più in questi link ...

How to get first EntityKey Name for an Entity in EF4

How can I extract the database table and column name for a property on an EF4 entity?


EDIT: Usando tua lista di navigationProperties un s punto di partenza, ho avuto tutto il necessario in questo modo:

 ManyToManyReferences = navigationProperties.Where(np => 
      np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many && 
      np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) 
      .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name)) 
      .ToList(); 

     OneToManyReferences = navigationProperties.Where(np => 
      (np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || 
      np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne) && 
      np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) 
      .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name)) 
      .ToList(); 

     ManyToOneReferences = navigationProperties.Where(np => 
      np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many && 
      (np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || 
      np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne)) 
      .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name)) 
      .ToList(); 

     OneToOneReferences = navigationProperties.Where(np => 
      np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One && 
      np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) 
      .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name)) 
      .ToList(); 

metodo CreateLambdaExpression non è mia cortesia, i crediti vanno a Jon Skeet, il codice è stato creato con l'aiuto di this answer

Ecco il mio metodo CreateLambdaExpression:

public static Expression<Func<TEntity, object>> CreateLambdaExpression<TEntity>(string propertyName) 
{ 
    ParameterExpression parameter = Expression.Parameter(typeof (TEntity), typeof (TEntity).Name); 
    Expression property = Expression.Property(parameter, propertyName); 

    return Expression.Lambda<Func<TEntity, object>>(property, new[] {parameter}); 
} 
+1

Grazie, questo g ho iniziato, e ho ottenuto ciò di cui avevo bisogno (senza riflessione), ho ampliato la tua risposta per mostrare come. –

+1

è grandioso, grazie anche a te - lo terrò nei miei link - avevo già un po 'di utilizzo in precedenza - ma non l'ho mai "elaborato" in troppi dettagli, questo è un pezzo di codice molto utile. E una buona domanda. – NSGaga

+0

Sembra che 'ForeignKeyDependents' e' ForeignKeyPrincipals' siano contrassegnati come 'internal' in' EntitySet' per EF6. –