【问题标题】:Generic way to retrieve Primary Keys from Navigation Properties从导航属性中检索主键的通用方法
【发布时间】:2013-12-03 08:12:16
【问题描述】:

给定一个实体,我想以通用方式检索相关实体的主键。 例如:对于客户,我想要一份其订单的 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,我可能应该使用其他东西,但我不确定是什么。

【问题讨论】:

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


    【解决方案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);
         }
    }
    

    如您所见,查找导航属性的标准是其包含的组件与您的主要类型相同。

    编辑

    好的,试试这个:

            // 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,它们正是主键名称和值。

    【讨论】:

    • Tnx 为您的回复。使用您的代码,我可以获取导航属性的 ID 名称,但不能获取其值。当我尝试使用 Context.Entry(entity) 获取 entityEntry 时,它失败了。我正在努力传递正确的类型。
    • Jowen,如果您需要一个值,您可以从一个对象中获取它,请执行以下操作:navEntity = prop.GetValue(entity, null) 然后 Context.Entry(navEntity)。还是我误会了什么?
    • 也许它就像你想象的那样简单,但我仍然无法让它工作。尝试填充 navEntity 时出现错误。写整个方法是不是太费劲了?我真的很感激!
    • 谢谢托尼!该代码有效。我只需要在 prop.GetValue(entity, null).. 中传递一个额外的 null,而 CreateEntityKey 很关键:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    相关资源
    最近更新 更多