문제

Given an entity, I would like to retrieve the primary keys of the related entities in a generic way. For instance: for a Customer, I would like to have a list of ID's of its Orders.

Current signature of my attempt:

GetPrimaryKeysOfRelatedEntities(DbContext db, object entity)

I'm able to retrieve the PK of an entity, and I'm able to retrieve navigation properties of an entity... but I can't get the PK of the navigation properties of an entity.

I'm missing a small link here!

Here's my code to get the Keys of entities, which doesn't work for navigation properties

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

This code uses EntityType, and I propably should use something else, but I'm not sure what.

도움이 되었습니까?

해결책

I can't see the problem, you have a great piece of code which can get primary keys of an entity by its type.

You can use a reflection to enumerate properties of your entity type and call your code for type of these properties. For ex like this:

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

As you can see the criteria for finding nav properties is that its containing assambly is the same as your primary type.

Edit

Ok, try this:

        // 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 method returns object of EntityKey class (http://msdn.microsoft.com/ru-ru/library/system.data.entitykey(v=vs.110).aspx) which has EntityKeyValues property which is an array of EntityKeyMember. EntityKeyMember has properties Key and Value which are exactly primary key name and value.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top