Entity framework 4 实体框架代码优先:如何在运行时确定用于导航属性的外键属性?

Entity framework 4 实体框架代码优先:如何在运行时确定用于导航属性的外键属性?,entity-framework-4,ef-code-first,Entity Framework 4,Ef Code First,我有一个实体框架代码FirstDBContext,配置了以下实体。在本例中,类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;

我有一个实体框架代码FirstDBContext,配置了以下实体。在本例中,类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; }
}
公共类Foo
{
公共Guid Id{get;set;}
公共虚拟ICollection条{get;set;}
}
公共类酒吧
{
公共Guid Id{get;set;}
公共Guid FooId{get;set;}
公共虚拟Foo Foo{get;set;}
}
现在我知道,在内部,实体框架理解Foo和Bar之间的关系是由外键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); 
var context=newfoobardbcontext();
var bar=context.Set().First();
//我希望此方法在传递表达式b=>b.Foo时返回bar.FooId
objectresult=MyService.GetForeignKeyValue(bar,b=>b.Foo);
现在在这个简单的例子中,我知道我可以得到bar.FooId并完成它。关键是我正在编写一个类,我相信上面指定的GetForeignKeyValue方法是用户最干净的接口


是否可以查询DbContext配置以确定哪个属性用作导航属性的外键?(假设有一个)

EF知道FooID是Foo对象属性的外键属性的默认方式是-有任何理由不能依赖相同的假设吗?如果是这样的话,只需加上“Id”,就可以了


如果您没有遵循命名约定,并且没有在模型上使用属性,我认为您运气不好。没有记录在案的从DbContext获取引用的方法(至少我找不到)。

我实际上能够使用类的方法确定外键属性

以下是我用来(或多或少)获取所需信息的代码:

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;
}
object[]GetForeignKeyPropertyValue(TEntity实体,表达式navigationProperty)
{
if(实体==null)
返回新对象[]{};
//在对象上下文中查找实体元数据。
//(假设您可以通过属性CurrentDbContext访问DbContext。)
var objectContext=(CurrentDbContext作为IOObjectContextAdapter);
var metadataNamespace=ObjectContextAdapter.GetType().Namespace;
var entityIdentity=metadataNamespace+““+typeof(tenty).Name;//HACK:这似乎可以用于检索实体的EntityType。
var entityMetadata=objectContext.MetadataWorkspace.GetItem(entityIdentity,DataSpace.CSpace);
//TODO:验证是否找到实体元数据。
//通过解析导航属性表达式中的名称来获取导航属性元数据。
var navigationPropertyName=GetPropertyName(navigationProperty);
var navigationPropertyMetadata=entityMetadata.NavigationProperties.FirstOrDefault(np=>np.Name==navigationPropertyName);
//TODO:(JMB)验证是否找到导航属性元数据。
//从导航属性中提取外键列。
var ForeignKeyPropertyMetadata=navigationPropertyMetadata.GetDependentProperties();
//为每个外键属性创建属性getter。
var foreignKeyPropertyGetters=ForeignKeyPropertyMetadata
.Select(propertyMetadata=>MakePropertyGetter(propertyMetadata.Name))
.ToArray();
//执行外键属性getter以获取指定实体的外键属性值。
var foreignKeyPropertyValues=ForeignKeyPropertyGatters
.选择(propertyGetter=>propertyGetter(实体))
.ToArray();
返回ForeignKeyPropertyValue;
}
静态字符串GetPropertyName(表达式导航属性)
{
var lambda=navigationProperty作为LambdaExpression;
var member=lambda.Body作为MemberExpression;
返回member.member.Name;
}
静态函数MakePropertyGetter(类型entityType,字符串propertyName)
{
var parameterExpression=Expression.Parameter(typeof(tenty),“entity”);
var propertyExpression=Expression.PropertyOrField(parameterExpression,propertyName);
var lambdaExpression=Expression.Lambda(propertyExpression,parameterExpression);
var lambdaFunction=lambdaExpression.Compile();
返回(Func)lambdaFunction;
}

有一种方法可以使用ObjectContext的MetadataWorkspace,这是DbModelBuilder填充的。