none
How do I get the Primary key name from an derived entity? [EF 4.2 and DbContext] RRS feed

  • Question

  • Hi all

    I am using Table per Type so I have a derived entity. I am trying to get the Primary Key from its base class:

    ...
    foreach (var dbEntry in this.ChangeTracker.Entries()
    {
    ...
    string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;
    
    }
    

    But I am getting the error:

    Sequence contains no matching element

    Exception Details: System.InvalidOperationException: Sequence contains no matching element

    Source Error:

    Line 196:
    Line 197:        //Get primary key value (If you have more than one key column, this will need to be adjusted)
    Line 198:        string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;

    Any ideas?

    Thank you! 


    Max
    Friday, January 13, 2012 10:03 PM

Answers

  • well since (for now) all our derived classes will inherit from a base class with "ID" as primary key we will just hard code it :-)

    string keyName = "ID";
    
    string primaryKeyValue = dbEntry.CurrentValues.GetValue<object>(keyName).ToString();
    

    Thank you

     


    Max
    • Marked as answer by Maximusdm Tuesday, January 24, 2012 1:07 AM
    Tuesday, January 24, 2012 1:07 AM

All replies

  • Hi Maximusdm;

    You state, "I am using Table per Type so I have a derived entity. I am trying to get the Primary Key from its base class:", It is the inherited value of the base type and you do not need to execute the code posted to get its value. But if you want its name you need to change the second parameter of GetCustomAttributes to true as shown below because the property is inherited from the base class.

     

    string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Count() > 0).Name;
    

     

     


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Saturday, January 14, 2012 6:28 PM
  • mmm I still get the same error msg after setting it to True :(
    Max
    Saturday, January 14, 2012 8:32 PM
  • Hi Max;

    Can you post your model / code so we can have a look.

     


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Saturday, January 14, 2012 8:44 PM
  • Hi Max,

    I agree @Fernando, you can refer my code here:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations;
    
    namespace TPTConcurrency
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (var db= new MyContext())
                {
                    var ps = db.Products.OfType<DiscontinuedProduct>().FirstOrDefault();
                    string keyName =db.Entry(ps).Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Count() > 0).Name;
                }
            }
        }
        public class Product
        {
            [Key]
            public int ProductId { get; set; }
            public string Name { get; set; }        
        }
        public class DiscontinuedProduct : Product
        {
            public DateTime DiscontinuedDate { get; set; }
            public Byte[] Timestamp { get; set; }
        }
        public class MyContext : DbContext
        {
            public DbSet<Product> Products { get; set; }
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Product>().ToTable("Products");
                modelBuilder.Entity<DiscontinuedProduct>().ToTable("OldProducts");
            }
        }
    }
    
    

    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.

    Monday, January 16, 2012 7:07 AM
    Moderator
  • Fernando/Alex,

    Perhaps my problem is that I should be calling:
    public IEnumerable<DbEntityEntry<TEntity>> Entries<TEntity>() where TEntity : class;

    In your code, you are calling: db.Entry(ps).Entity.GetType() and I am calling: db.Entry.Entity.GetType()

    Not sure how I can adapt it to my code though... I am looping though the ChangeTracker and not all entities will be derived so I am not using OfType<Entity>...here is my code:

     

    DbContext:
    
        public int SaveChanges(Guid userID)
        {
            foreach (var entry in this.ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Added ||
                                                                        p.State == System.Data.EntityState.Deleted || p.State == System.Data.EntityState.Modified))
            {
                //For each changed record, get the audit record entries and add it
                foreach (AuditLog x in GetAuditRecords(entry, userID))
                {
                    this.AuditLogs.Add(x);
                }
            }
    
            return base.SaveChanges();
        }
    
    
        private List<AuditLog> GetAuditRecords(DbEntityEntry dbEntry, Guid userID)
        {
            List<AuditLog> result = new List<AuditLog>();
    
            DateTime currentTime = DateTime.UtcNow;
    
            //Get the Table() attribute, if one exists 
            TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
    
            //Get table name (if it has a Table attribute, use that, otherwise get the pluralized name) 
            string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
    
            //Get primary key value (If you have more than one key column, this will need to be adjusted) 
            keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Count() > 0).Name;
    ...
    
    
    

    Thank you


    Max
    Monday, January 16, 2012 3:59 PM
  •  

    Unless we can see the structure of your model in your code it is hard to say what is going on. So please post your model / code so we can have a look so we can offer a solution.


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Monday, January 16, 2012 4:08 PM
  • There you go Fernando:

      public abstract class BaseEntity : ICreated
      {
        private DateTime created;
    
        public DateTime Created
        {
          get
          {
            if (created == DateTime.MinValue)
            {
              created = DefaultValues.UtcNow;
            }
            return (created);
          }
          set
          {
            created = value;
          }
        }
    
        public Guid CreatedByID { get; set; }
    
        //navigation properties
        public virtual User CreatedBy { get; set; }
    
    
      public abstract class GuidIDEntity : BaseEntity, IGuidID, IModified, IName
      {
        private Guid id;
    
        public Guid ID
        {
          get
          {
            if (id == Guid.Empty)
            {
              id = DefaultValues.NewID;
            }
            return (id);
          }
          set
          {
            id = value;
          }
        }
    
        public Nullable<DateTime> Modified { get; set; }
        public Nullable<Guid> ModifiedByID { get; set; }
        public string Name { get; set; }
    
        //Navigation properties
        public virtual User ModifiedBy { get; set; }
      }
    
    
      public abstract class Entity : GuidIDEntity
      {
        public Nullable<Guid> ParentID { get; set; }
    
        //Navigation properties
        public virtual Entity Parent { get; set; }
      }
    
      public class SccmSite : Entity
      {
        public string Code { get; set; }
        public string SiteSize { get; set; }
      }
    ...
    public class OtherEntities: Entity
    {
    }
    

    Thank you


    Max
    Monday, January 16, 2012 5:15 PM
  • Hi Max,

     using (var db= new MyContext())
                {
                    var ps = db.Products.OfType<DiscontinuedProduct>().FirstOrDefault();
                    ps.DiscontinuedDate = DateTime.Now;
                    foreach (var entry in db.ChangeTracker.Entries().Where(p=>p.State== System.Data.EntityState.Modified))
                    {
                        string keyName = entry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Count() > 0).Name;
                        Console.WriteLine(keyName);
                    }                
                }
    
    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.

    Tuesday, January 17, 2012 8:50 AM
    Moderator
  • mmm I am not really sure what changes you added here. The command line seems the same one you posted before.
    Am I missing something?

    Thanks


    Max
    Tuesday, January 17, 2012 6:17 PM
  • Hi Max,

    I'm not sure how do you retrieve the derived Type in your TPT scenario. According to your code in your previous post, you're looping the ChangeTracker to find any ^Unchanged entries, right? My lastest post just demos modified entry for my DbContext----->entry.Entity.GetType()

    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.

    Wednesday, January 18, 2012 3:44 AM
    Moderator
  • well since (for now) all our derived classes will inherit from a base class with "ID" as primary key we will just hard code it :-)

    string keyName = "ID";
    
    string primaryKeyValue = dbEntry.CurrentValues.GetValue<object>(keyName).ToString();
    

    Thank you

     


    Max
    • Marked as answer by Maximusdm Tuesday, January 24, 2012 1:07 AM
    Tuesday, January 24, 2012 1:07 AM
  • Hi

    Did you find the solution to this problem. I am facing the same issue.

    Thankyou


    AmeeraSajid


    • Edited by AmeeraSajid Tuesday, August 6, 2013 6:45 AM
    Tuesday, August 6, 2013 6:45 AM
  • Have you tried marking your ID property with the [Key] attribute?

    Tuesday, October 8, 2013 11:14 AM