Entity Framework Code First: how can I determine the foreign key property used for the navigation property at runtime?

I have Entity Framework First DbContext code with settings for the following objects. In this example, the Bar class is a child of the Foo class.

public class Foo { public Guid Id { get; set; } public virtual ICollection<Bar> Bars { get; set; } } public class Bar { public Guid Id { get; set; } public Guid FooId { get; set; } public virtual Foo Foo { get; set; } } 

Now I know that internally, the Entity Framework understands that the relationship between Foo and Bar is determined by the foreign key of Bar.FooId. What I would like to do is somehow extract this relationship at runtime using expressions. I would like to implement a method that behaves as follows:

 var context = new FooBarDbContext(); var bar = context.Set<Bar>().First(); // I want this method to return bar.FooId when passed the expression b => b.Foo object result = MyService.GetForeignKeyValue(bar, b => b.Foo); 

Now in this simplified example, I know that I can just get bar.FooId and do it. The fact is that I am writing a class for which I believe that the GetForeignKeyValue method mentioned above is the cleanest user interface.

Is it possible to query the DbContext configuration to determine which property is used as a foreign key for the navigation property? (Suppose there is one)

+4
source share
2 answers

I really was able to determine the properties of the foreign key using the GetDependentProperties NavigationProperty method.

Here is the code I used (more or less) to get what I need:

 object[] GetForeignKeyPropertyValues<TEntity, TRelatedEntity>(TEntity entity, Expression<Func<TEntity, TRelatedEntity>> navigationProperty) { if (entity == null) return new object[] { }; // Find the entity metadata in the object context. // (Assume you have access to the DbContext through the property CurrentDbContext.) var objectContext = (CurrentDbContext as IObjectContextAdapter).ObjectContext; var metadataNamespace = ObjectContextAdapter.GetType().Namespace; var entityIdentity = metadataNamespace + "." + typeof(TEntity).Name; // HACK: This seems to work to retrieve the EntityType for an entity. var entityMetadata = objectContext.MetadataWorkspace.GetItem<EntityType>(entityIdentity, DataSpace.CSpace); // TODO: Verify that the entity metadata was found. // Get the navigation property metadata by parsing the name from the navigation property expression. var navigationPropertyName = GetPropertyName(navigationProperty); var navigationPropertyMetadata = entityMetadata.NavigationProperties.FirstOrDefault(np => np.Name == navigationPropertyName); // TODO: (JMB) Verify that the navigation property metadata was found. // Extract the foreign key columns from the navigation property. var foreignKeyPropertyMetadatas = navigationPropertyMetadata.GetDependentProperties(); // Create property getters for each foreign key property. var foreignKeyPropertyGetters = foreignKeyPropertyMetadatas .Select(propertyMetadata => MakePropertyGetter<TEntity>(propertyMetadata.Name)) .ToArray(); // Execute the foreign key property getters to get the foreign key property values for the specified entity. var foreignKeyPropertyValues = foreignKeyPropertyGetters .Select(propertyGetter => propertyGetter(entity)) .ToArray(); return foreignKeyPropertyValues; } static string GetPropertyName<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> navigationProperty) { var lambda = navigationProperty as LambdaExpression; var member = lambda.Body as MemberExpression; return member.Member.Name; } static Func<TEntity, object> MakePropertyGetter<TEntity>(Type entityType, string propertyName) { var parameterExpression = Expression.Parameter(typeof(TEntity), "entity"); var propertyExpression = Expression.PropertyOrField(parameterExpression, propertyName); var lambdaExpression = Expression.Lambda(propertyExpression, parameterExpression); var lambdaFunction = lambdaExpression.Compile(); return (Func<TEntity, object>)lambdaFunction; } 
+5
source

By default, EF knows that FooID is a foreign key property for a property of a Foo object: based on a naming convention - is there any reason you can't rely on the same assumption? If so, just get the string represented by your expression parameter , add "Id" and you're good.

If you donโ€™t follow the naming convention and use attributes on your model, I think you're out of luck. There is no documented method for getting a DbModelBuilder link from a DbContext (at least not what I can find).

-3
source

Source: https://habr.com/ru/post/1479751/


All Articles