从导航属性检索主键的通用方法

时间:2013-11-27 15:53:20

标签: c# entity-framework-4 navigation-properties

给定一个实体,我想以通用方式检索相关实体的主键。 例如:对于客户,我想要一份其订单ID的列表。

我尝试的当前签名:

GetPrimaryKeysOfRelatedEntities(DbContext db, object entity)

我能够检索实体的PK,并且我能够检索实体的导航属性......但我无法获得实体导航属性的PK。

我在这里错过了一个小链接!

这是获取实体密钥的代码,它不适用于导航属性

private static IEnumerable<string> GetEntityType(DbContext db, Type entityType)
{
    entityType = ObjectContext.GetObjectType(entityType);

    var metadataWorkspace = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
    var objectItemCollection = (ObjectItemCollection)metadataWorkspace.GetItemCollection(DataSpace.OSpace);

    ReadOnlyCollection<EntityType> entityTypes = metadataWorkspace.GetItems<EntityType>(DataSpace.OSpace);

    if (entityTypes == null)
    {
        throw new InvalidOperationException();
    }

    var ospaceType = entityTypes.SingleOrDefault(t => objectItemCollection.GetClrType(t) == entityType);

    if (ospaceType == null)
    {
        throw new ArgumentException(
            string.Format("The type '{0}' is not mapped as an entity type.", entityType.Name), "entityType");
    }

    return ospaceType.KeyMembers.Select(k => k.Name);
}

此代码使用EntityType,我应该使用其他东西,但我不确定是什么。

1 个答案:

答案 0 :(得分:1)

我看不出问题,你有一段很棒的代码可以按类型获取实体的主键。

您可以使用反射来枚举实体类型的属性,并为这些属性的类型调用代码。就像这样:

var entityType = entity.GetType(); // or another type source
foreach (var prop in type.GetProperties())
{
     if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericArguments().Any(x => x.Assembly == type.Assembly))
     {
         var navPropType = prop.PropertyType.GetGenericArguments().First(x => x.Assembly == type.Assembly);
         var keysForThisNavPropType = GetEntityType(db, navPropType);
     }
     else if (prop.PropertyType.Assembly == type.Assembly)
     {
         var keysForThisNavPropType = GetEntityType(db, prop.PropertyType);
     }
}

正如您所看到的,查找导航属性的条件是其包含的assambly与您的主要类型相同。

修改

好的,试试这个:

        // we need DbContext vaule
        var db = YOUR_DB_CONTEXT; // assing db context here
        // So we can get ObjectContext instance
        var ctx = ((IObjectContextAdapter) db).ObjectContext;
        // we need some entity to check
        object entity = someYourEntity; // assign your entity here
        // let's get its type
        var type = entity.GetType();

        // helper function to get set name
        Func<Type, ObjectContext, string> getEntitySetByObjectType = (t, context) =>
            {
                var container =
                    context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);

                var entitySet =
                    container.BaseEntitySets.First(item => item.ElementType.Name.Equals(t.Name));

                return container.Name + "." + entitySet.Name;
            };

        // go through the entity's properties
        foreach (var prop in type.GetProperties())
        {
            // nav properties which are collections
            if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericArguments().Any(x => x.Assembly == type.Assembly))
            {
                var val = (IEnumerable)prop.GetValue(entity);
                if (val != null)
                {
                    // get value and check if it is not null
                    string setName = null;
                    // go through collection values
                    foreach (var obj in val)
                    {
                        if (setName == null)
                            setName = getEntitySetByObjectType(obj.GetType(), ctx);

                        // get primary key values
                        var entityKey = ctx.CreateEntityKey(setName, obj);
                        Console.WriteLine(entityKey);
                    }
                }
            }
            // nav props which are single objects
            else if (prop.PropertyType.Assembly == type.Assembly)
            {
                // get value and check if it is not null
                var val = prop.GetValue(entity);
                if (val != null)
                {
                    // get primary key values
                    var entityKey = ctx.CreateEntityKey(getEntitySetByObjectType(prop.PropertyType, ctx), val);
                    Console.WriteLine(entityKey);
                }
            }
        }

CreateEntityKey方法返回EntityKey类(http://msdn.microsoft.com/ru-ru/library/system.data.entitykey(v=vs.110).aspx)的对象,该类具有EntityKeyValues属性,该属性是EntityKeyMember的数组。 EntityKeyMember具有属性Key和Value,它们是主键名和值。