实体框架代码首先:如何在运行时确定用于导航属性的外键属性? [英] Entity Framework Code First: How can I determine the foreign key property used for a navigation property at runtime?

查看:95
本文介绍了实体框架代码首先:如何在运行时确定用于导航属性的外键属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个实体框架代码第一个DbContext,配置了以下实体。在这个例子中,类Bar是Foo类的孩子。

  public class Foo 
{
public Guid Id {get;组; }

public virtual ICollection< Bar>酒吧{get;组; }
}

public class Bar
{
public Guid Id {get;组; }

public Guid FooId {get;组; }

public virtual Foo Foo {get;组; }
}

现在我知道内部实体框架了解到Foo和Bar由外键Bar.FooId定义。我想做的是以某种方式在运行时使用表达式来提取该关系。我想实现一个行为如下的方法:

  var context = new FooBarDbContext(); 
var bar = context.Set< Bar>()。First();

//我希望这个方法在传递表达式时返回bar.FooId b => b.Foo
object result = MyService.GetForeignKeyValue(bar,b => b.Foo);

现在在这个简单的例子中,我知道我可以直接获取bar.FooId并完成。关键是我正在写一个类,我相信上面指定的GetForeignKeyValue方法是用户最干净的界面。



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

解决方案

我实际上能够使用 GetDependentProperties 方法的 NavigationProperty 类。



这是我使用的代码(或多或少)来获得我需要的东西:

  object [] GetForeignKeyPropertyValues< TEntity,TRelatedEntity>(TEntity entity,Expression< Func< TEntity,TRelatedEntity>> navigationProperty)
{
if(entity == null)
return new object [] {};

//在对象上下文中找到实体元数据。
//(假设您可以通过属性CurrentDbContext访问DbContext。)
var objectContext =(CurrentDbContext as IObjectContextAdapter).ObjectContext;

var metadataNamespace = ObjectContextAdapter.GetType()。Namespace;

var entityIdentity = metadataNamespace +。 + typeof(TEntity).Name; // HACK:这似乎可以检索实体的EntityType。
var entityMetadata = objectContext.MetadataWorkspace.GetItem< EntityType>(entityIdentity,DataSpace.CSpace);

// TODO:验证是否找到了实体元数据。

//通过从导航属性表达式解析名称获取导航属性元数据。
var navigationPropertyName = GetPropertyName(navigationProperty);
var navigationPropertyMetadata = entityMetadata.NavigationProperties.FirstOrDefault(np => np.Name == navigationPropertyName);

// TODO :( JMB)验证是否找到了导航属性元数据。

//从导航属性中提取外键列。
var foreignKeyPropertyMetadatas = navigationPropertyMetadata.GetDependentProperties();

//为每个外键属性创建属性getter。
var foreignKeyPropertyGetters = foreignKeyPropertyMetadatas
.Select(propertyMetadata => MakePropertyGetter< TEntity>(propertyMetadata.Name))
.ToArray();

//执行外键属性getters以获取指定实体的外键属性值。
var foreignKeyPropertyValues = foreignKeyPropertyGetters
.Select(propertyGetter => propertyGetter(entity))
.ToArray();

return foreignKeyPropertyValues;


静态字符串GetPropertyName< TEntity,TProperty>(表达式< 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;
}


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
{
    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, Entity Framework understands that the relationship between Foo and Bar is defined by the foreign key Bar.FooId. What I'd like to do is to somehow extract that relationship at runtime using expressions. I'd 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 simplistic example I know that I could just get bar.FooId and be done. The point is that I'm writing a class for which I believe the GetForeignKeyValue method specified above is the cleanest interface for a user.

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

解决方案

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

这篇关于实体框架代码首先:如何在运行时确定用于导航属性的外键属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆