none
How to get the matching foreign key property from a Navigation property RRS feed

  • Question

  • I have an auditing framework I wrote a while back for LINQ to SQL, but when I ported it to EF it had some limitations over the L2S version.

    I am using EF 4.1 Code-first in this example but I think the solution would be the same, if I were using an EDMX or not.

    I have an Entity defined below, with AddressId and Address properties. These 2 properties are wired up together.

    public class Claim
    {
        [Required, StringLength(200)]
        public string CompanyName { get; set; }
    
        public int AddressId { get; set; }
    
        public virtual Address Address { get; set; }
    }
    

    Now what I want to do is say, given a Navigation property, how can I find the matching foreigh key property. For example, given the Claim Address property, how can I query the EF MetadataWorkspace to locate it's matching AddressId FK property.

    A long time I started playing with this, and came up with something along these lines... Although it's totally not working and I gave up on it a while back, but now I'd really like to solve it.

     

      public string GetEntityRelationshipKeyName<T, TR>()
        {
          Type entityType = typeof(T);
          Type relType = typeof(TR);
    
          EntityType type = (from meta in ObjectContext.MetadataWorkspace.GetItems(DataSpace.CSpace)
                    where meta.BuiltInTypeKind == BuiltInTypeKind.EntityType
                    select meta)
                   .OfType<EntityType>()
                   .Where(e => e.Name == relType.Name).Single();
          
          //foreach (IRelatedEnd relatedEnd in ((IEntityWithRelationships)entityType).RelationshipManager.GetAllRelatedEnds())
          //{
          //  foreach (IEntityWithKey relatedItem in relatedEnd)
          //  {
          //    object reference = null;
          //    entities.TryGetObjectByKey(relatedItem.EntityKey, out reference);
          //    (original as EntityObject).GetType().GetProperty(relatedEnd.TargetRoleName).SetValue(original, reference, null);
          //  }
          //}
    
    
          //var navigationProperty = type.NavigationProperties.First(n => n.DeclaringType.Name == relType.Name);
          //return navigationProperty.ToEndMember.Name;
            }

    Thanks!

     

     

    Wednesday, August 3, 2011 8:38 PM

Answers

  • Hi Matt - tracking navigation properties in that way isn't supported; the GetObjectStateEntry API also handles relationships; in that case, the CurrentValues and OriginalValues correspond to the EntityKeys.
    -adi

    Adi Unnithan | Software Design Engineer | SQL Server Developer Tools | Please mark the post as answered if it sufficiently answers your question
    Friday, August 5, 2011 9:03 PM

All replies

  • Hi Matt,

    Welcome!

    I'm not sure about your question, I write a sample and hope it can help, please clarify me if I misunderstood.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity;
    
    namespace ConsoleApplication5
    {
      class Program
      {
        static void Main(string[] args)
        {
          using (var context= new Context())
          {
            // context.Database.CreateIfNotExists();
            // var address = new Address { AddressId=1, Place="china", Claims=new List<Claim>() };
            //var claim = new Claim { ClainId=1, CompanyName="microsoft" };
            //address.Claims.Add(claim);
            //context.Addressesd.Add(address);
            //context.SaveChanges();
            var claim = context.Claims.Find(1);
            var foriegh= claim.Address.AddressId;
          }
        }
      }
      public class Claim
      {
        [Key]
        public int ClainId { get; set; }
        [Required, StringLength(200)]
        public string CompanyName { get; set; }
    
        public int AddressId { get; set; }
    
        public virtual Address Address { get; set; }
      }
      public class Address
      {
        [Key]
        public int AddressId { get; set; }
        public string Place { get; set; }
        public virtual ICollection<Claim> Claims { get; set; }
      }
      public class Context:DbContext
      {
        public DbSet<Claim> Claims { get; set; }
        public DbSet<Address> Addressesd { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
          modelBuilder.Entity<Address>().HasMany(a => a.Claims).WithRequired(c => c.Address).HasForeignKey(c => c.AddressId);
        }
      }
    }
    

    I think we can use Navigation to find the foreign key in related entity's property.

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, August 4, 2011 7:29 AM
    Moderator
  • I know MetadataWorkspace is pretty generic for this type of stuff; I’ll try both getting FK from the nav prop and vice versa:

                // Getting FK given nav prop

                EntityType claimEt = c.GetItem<EntityType>("Model1.Claim");

                NavigationProperty addressNavProp = claimEt.NavigationProperties.First();

                RelationshipEndMember endMember = addressNavProp.ToEndMember;

                AssociationType assoc = endMember.DeclaringType as AssociationType;

                var dependentProps = assoc.ReferentialConstraints.First().ToProperties.ToList();

                dependentProps.ForEach(p => Console.WriteLine(p.Name));

     

                // Getting nav prop given FK

                EdmProperty fkProp = dependentProps.First();

     

                // We have to check all associations on owning entity type via available nav props

                EntityType owningType = (EntityType)fkProp.DeclaringType;

                var navProps =

                    owningType

                    .NavigationProperties

                    .Where((np) =>

                    {

                        AssociationType assocForNavProp = (AssociationType)(np.ToEndMember.DeclaringType);

                        return assocForNavProp.ReferentialConstraints.First().ToProperties.Contains(fkProp);

                    });

                navProps.ToList().ForEach(np => Console.WriteLine(np.Name));

     

    FWIW, you could also use public documented extension methods in the Microsoft.Data.Entity.Design.DatabaseGeneration assembly if you have VS installed. You can use that assembly outside of VS too (no VS deps). I’m sure the ecosystem has come up with other extensions too.

    Hope that helps,

    adi


    Adi Unnithan | Software Design Engineer | SQL Server Developer Tools | Please mark the post as answered if it sufficiently answers your question
    Friday, August 5, 2011 8:00 PM
  • Hey Adi thanks a ton for the response.

    Now that I've got this part figured out, in order for my auditing framework to be "complete" I will need full OriginalValue and CurrentValue change-tracking. I have this entirely working for scalar and complex properties, but I am not sure how to achieve this with Navigation properties.

    If I try

    var entry = ObjectContext.ObjectStateManager.GetObjectStateEntry(entity);
    entry.OriginalValues["ANavigationProperty"] // it blows up
    
    
    // If I try using the code-first method
    var ref = Entry(entity).Reference("ANavigationProperty");
    var current= ref.CurrentValue;
    var orig = ref.OriginalValue; // This property does not exist
    


    So my final question is: Is it possible to get the original and current values from the ChangeTracker for NavigationProperties?
    Even if it means I can access the EntityKey's from both of them and have to query the DB manually?

    If the latter is possible could you provide how I might obtain the Original and Current EntityKey's from the NavigationProperty, and if necessary how to execute a query on the DB with these keys?

     

     

     

    Friday, August 5, 2011 8:06 PM
  • Hi Matt - tracking navigation properties in that way isn't supported; the GetObjectStateEntry API also handles relationships; in that case, the CurrentValues and OriginalValues correspond to the EntityKeys.
    -adi

    Adi Unnithan | Software Design Engineer | SQL Server Developer Tools | Please mark the post as answered if it sufficiently answers your question
    Friday, August 5, 2011 9:03 PM