Created
October 18, 2020 15:30
-
-
Save jods4/2f44393ea20764682be31f329f545d84 to your computer and use it in GitHub Desktop.
Finds the inverse Property of an EF 6 NavigationProperty.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Data.Entity; | |
using System.Data.Entity.Core.Metadata.Edm; | |
using System.Data.Entity.Infrastructure; | |
using System.Reflection; | |
class Program | |
{ | |
static PropertyInfo GetInverseProperty(DbContext context, Type parentType, string parentProperty) | |
{ | |
var meta = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace; | |
// Note: a CLR class can have a null Namespace (if it isn't defined in one), | |
// but EDM throws NullArgumentException and expects an empty string instead. | |
// TODO: if this code can be called from contexts where `parentType` isn't guaranteed to be | |
// a mapped EF class, you may want to call `TryGetType` and handle the error instead. | |
var oType = meta.GetType(parentType.Name, parentType.Namespace ?? "", DataSpace.OSpace) as EntityType; | |
// TODO: if this code can be called from contexts where `parentProperty` isn't guaranteed to be | |
// a mapped EF navigation property, you may want to call `TryGetValue` and handle the error instead. | |
var navigation = oType.NavigationProperties.GetValue(parentProperty, ignoreCase: false); | |
var cType = (AssociationType)meta.GetEdmSpaceType(navigation.RelationshipType); | |
// TODO: Could it sometimes be [0] ? | |
// Maybe it's safer to check if the property is different from the source property? | |
// Don't check the type though, as it could be a self-referencing relationship. | |
var endMember = (AssociationEndMember)cType.RelationshipEndMembers[1]; | |
return endMember.MetadataProperties.TryGetValue("ClrPropertyInfo", ignoreCase: false, out var metaProperty) | |
? (PropertyInfo)metaProperty.Value | |
: null; // No inverse property | |
} | |
static void Main(string[] args) | |
{ | |
var db = new Context(); | |
var foo = typeof(Foo); | |
Console.WriteLine(GetInverseProperty(db, foo, nameof(Foo.Bars))); | |
Console.WriteLine(GetInverseProperty(db, foo, nameof(Foo.Children))); | |
} | |
} | |
// Fake data model for demonstration: | |
class Foo | |
{ | |
public int Id { get; set; } | |
// This one has an inverse nav | |
public ICollection<Bar> Bars { get; set; } | |
// This one doesn't | |
public ICollection<Child> Children { get; set; } | |
} | |
class Bar | |
{ | |
public int Id { get; set; } | |
public int FooId { get; set; } | |
public Foo Foo { get; set; } | |
} | |
class Child | |
{ | |
public int Id { get; set; } | |
public int FooId { get; set; } | |
} | |
class Context : DbContext | |
{ | |
public DbSet<Foo> Foos { get; set; } | |
public DbSet<Bar> Bars { get; set; } | |
public DbSet<Child> Children { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment