Shelby115
Shelby115

Reputation: 2867

How to distinguish Navigation Properties from regular ones when using GetProperties?

Is there a way to distinguish between a regular collection property and a navigation property on an Entity-Framework class (Database-First)?

I'm currently checking if the object is ICollection and IsVirtual but I feel this could possibly trigger on a regular property that someone has declared as a virtual collection.

Question: Are there any other ways to distinguish Navigation Properties from others?

Context: I'm using this to compare values of any object, but I want it to ignore navigation properties (to ignore circular-references among other things).

foreach (var item in (IEnumerable)obj)
{
    list2.MoveNext();
    var item2 = list2.Current;
    foreach (PropertyInfo propInfo in item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        Object v1 = propInfo.GetValue(item);
        Object v2 = propInfo.GetValue(item2);

        Primitive = (v1 == null && v2 == null) || IsPrimitive(v1.GetType());

        if (Primitive)
        {
            Assert.AreEqual(v1, v2);
        }
        else
        {
            // Ignore Navigation Properties
            // Currently assuming Virtual properties to be Navigation...
            if (propInfo.GetGetMethod().IsVirtual) continue;
            CompareObjects(v1, v2);
        }
    }
}

Upvotes: 7

Views: 3119

Answers (2)

ocuenca
ocuenca

Reputation: 39356

Well, if you want to know the names of the navigation properties and the scalar properties related to an Entity, I suggest you use this code:

using (var db = new YourContext())
{
    var workspace = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
    var itemCollection = (ObjectItemCollection)(workspace.GetItemCollection(DataSpace.OSpace));
    var entityType = itemCollection.OfType<EntityType>().Single(e => itemCollection.GetClrType(e) == typeof(YourEntity));
    
    foreach (var navigationProperty in entityType.NavigationProperties)
    {
        Console.WriteLine(navigationProperty.Name);
    }
    
    foreach (var property in entityType.Properties)
    {
        Console.WriteLine(property.Name);
    }
}

Upvotes: 4

RitchieD
RitchieD

Reputation: 1861

One solution when using GetProperties() is to create an IEntity interface and apply it to all entities. Then you can skip over single entity navigation properties by checking if they implement IEntity and multiple entity navigation if they are of type ICollection.

So in your foreach,

if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>)) continue;
if (property.PropertyType.GetInterfaces().Contains(typeof(IEntity))) continue;

Here is a simple method to return only updateable properties using this logic:

private IEnumerable<PropertyInfo> GetUpdateableProperties<T>(T entity) where T : IEntity
    {
        return entity.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty)
            .Where(property =>
                property.CanWrite &&
                !property.PropertyType.GetInterfaces().Contains(typeof(IEntity)) &&
                !(property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
                );
    }

Upvotes: 2

Related Questions