views:

64

answers:

2

I am trying to create a generic method that will retrieve an item by its id:

public T GetByID(int id)
{
    return (T) context.GetObjectByKey(
        new System.Data.EntityKey(context.DefaultContainerName + "." +
             context.CreateObjectSet<T>().EntitySet.Name, 
             "ProductID", id));
}

Basically I am able to infer the entity name from T, however I have no idea how to figure out what the primary key is for an entity?

+2  A: 

Here's a snippet I have.. I hope it helps. Now that I look at it, I think I could improve it as well.

    _qualifiedTypeName = _context.DefaultContainerName + "." + _baseTypeName;
    Type baseType = GetBaseType(typeof(T));
    _baseTypeName = baseType.Name.ToString();

    PropertyInfo[] entityProperties = baseType.GetProperties();
    List<string> keyList = new List<string>();

    foreach (PropertyInfo prop in entityProperties) 
    {
      object[] attrs = prop.GetCustomAttributes(false);
      foreach (object obj in attrs)                
      {
        if (obj.GetType() == typeof(EdmScalarPropertyAttribute))
        {
          EdmScalarPropertyAttribute attr = (EdmScalarPropertyAttribute)obj;
          if (attr.EntityKeyProperty) keyList.Add(prop.Name);
         }
       }
    }
    if (keyList.Count > 0)
    {
      _keyName = keyList[0];
    }

and then you'd return it like:

EntityKey key = new EntityKey(_qualifiedTypeName, _keyName, id);
return (T)_context.GetObjectByKey(key);

and it looks like I got the above from here: http://blog.vascooliveira.com/how-to-check-for-an-entity-objects-entitykey-properties/

itchi
My hunch is that this only works with entities that are generated by EF and not POCO or Self Tracking POCO classes that are generated by T4 templates. The only attribute that decorates the properties there is [DataMember] which doesn't really help me in this case.
e36M3
oh interesting, my apologies. Although the T4 template certainly knows.. if (ef.IsKey(edmProperty)) Maybe hack the T4 template to add your own annotation?
itchi
Agreed, I was thinking the same.
e36M3
+1  A: 

I ended up creating my own attribute and modifying the T4 template to place that attribute above the primary key column. Here are the steps I took:

  1. Add the following above the [DataMember] attribute in the T4 template:
    <#if (ef.IsKey(edmProperty)) {#> [PrimaryKeyAttribute] <#}#>
  2. Create the PrimaryKeyAttribute:

    [AttributeUsage(AttributeTargets.Property)]
    public class PrimaryKeyAttribute : Attribute
    {}
  1. Introduce a helper method to determine the primary key of an entity:

        private string GetPrimaryKey()
        {
            string primaryKey = string.Empty;

            PropertyInfo[] entityProperties = typeof(K).GetProperties();

            foreach (PropertyInfo prop in entityProperties)
            {
                object[] attrs = prop.GetCustomAttributes(false);
                foreach (object obj in attrs)
                {
                    if (obj.GetType() == typeof(PrimaryKeyAttribute))
                    {
                        primaryKey = prop.Name;
                        break;
                    }
                }
            }

            if (string.IsNullOrEmpty(primaryKey))
                throw new Exception("Cannot determine entity's primary key");

            return primaryKey;
        }
  1. Finally write the generic GetByID as such:
        public T GetByID(int id)
        {
            return (T)context.GetObjectByKey(new EntityKey(context.DefaultContainerName 
                                                + "." + context.CreateObjectSet().EntitySet.Name
                                                , GetPrimaryKey(), id));            
        }
e36M3
Sorry about the numbering of the steps, hopefully someone with more SO posting experience can fix it. Thank you.
e36M3