2013-05-08 37 views
4

我有一個實體框架代碼第一個DbContext與下列實體配置。在這個例子中,Bar類是Foo類的子類。實體框架代碼優先:如何在運行時確定用於導航屬性的外鍵屬性?

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; } 
} 

現在我知道在內部,Entity Framework明白Foo和Bar之間的關係是由外鍵Bar.FooId定義的。我想要做的是以某種方式在運行時使用表達式來提取該關係。我想實現的是表現爲如下的方法:在我知道我可以只得到bar.FooId和做這個簡單的例子

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); 

現在。重點是我正在編寫一個類,我相信上面指定的GetForeignKeyValue方法是用戶最乾淨的接口。

是否可以查詢DbContext配置以確定哪個屬性用作導航屬性的外鍵? (假設有一個)

回答

5

我實際上可以通過使用NavigationProperty類的GetDependentProperties方法來確定外鍵屬性。

這裏是我使用的(或多或少),以獲得我所需要的代碼:

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; 
} 
-3

EF知道FooID是Foo對象屬性的外鍵屬性的默認方式是based on a naming convention - 任何你不能依賴相同假設的理由?如果是這樣,只需get the string that your expression parameter represents,附加「Id」,而且你很好。

如果你沒有遵循命名約定,並且你沒有在模型上使用屬性,我認爲你運氣不好。沒有記錄的方法從DbContext獲取DbModelBuilder引用(至少不是我能找到的)。

+0

有使用的ObjectContext的MetadataWorkspace,這是DbModelBuilder填充有什麼辦法。 – 2013-10-25 14:33:04