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

前端 未结 2 508
礼貌的吻别
礼貌的吻别 2021-02-04 18:39

I have an Entity Framework Code First DbContext with the following entities configured. In this example class Bar is a child of class Foo.

public class Foo
{
            


        
相关标签:
2条回答
  • 2021-02-04 19:09

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

    If you aren't following the naming convention and you aren't using attributes on your model, I think you're out of luck. There's no documented method of getting the DbModelBuilder reference from the DbContext (at least not that I can find).

    0 讨论(0)
  • 2021-02-04 19:17

    I was actually able to determine the foreign key properties by using the GetDependentProperties method of the NavigationProperty class.

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

    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;
    }
    
    0 讨论(0)
提交回复
热议问题