views:

273

answers:

2

I am trying to create a generic method using EF4 to find the primary key of an object.

example

public string GetPrimaryKey() { .... }

To Give more info I am working off of the Tekpub StarterKit and below is the class I am trying to get up and running

using System;

using System.Collections.Generic; using System.Linq; using System.Web; using System.Data.Objects; using System.Data.Objects.ELinq; using System.Data.Linq; using Web.Infrastructure.Storage.EF4;

namespace Web.Infrastructure.Storage { public class EFSession:ISession { PuzzleEntities _db;//This is an ObjectContext public EFSession() { _db = new PuzzleEntities(); }

    public void CommitChanges() {
        _db.SaveChanges();
    }
    /// <summary>
    /// Gets the table provided by the type T and returns for querying
    /// </summary>
    private ObjectSet<T> GetObjectSet<T>() where T:class {
        return _db.CreateObjectSet<T>();
    }

    private T GetByPrimaryKey<T>() where T: class
    {
        .....
    }


    public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T: class{

        foreach (T item in All<T>().Where(expression))
        {
            GetObjectSet<T>().DeleteObject(item);
        }
    }

    public void Delete<T>(T item) where T : class {
        GetObjectSet<T>().DeleteObject(item);
    }

    public void DeleteAll<T>() where T : class {
        foreach(T item in All<T>())
        {
            GetObjectSet<T>().DeleteObject(item);
        }
    }

    public void Dispose() {
        _db.Dispose();
    }

    public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T:class {
        return GetObjectSet<T>().SingleOrDefault(expression);
    }

    public IQueryable<T> All<T>() where T : class {
        return GetObjectSet<T>().AsQueryable();
    }

    public void Add<T>(T item) where T : class {
        GetObjectSet<T>().AddObject(item);
    }
    public void Add<T>(IEnumerable<T> items) where T : class {
        foreach (T item in items)
        {
            GetObjectSet<T>().AddObject(item);
        }
    }
    public void Update<T>(T item) where T : class {
        //nothing needed here
    }

}

}

+3  A: 

There is a property on each EF4 entity called EntityKey which contains an array of EntityKeyValues (array is there in case of compound key).

You could reference this directly on your entity instance or create a generic helper method that does this under the covers. If I can test-drive some sample code, I'll post it up here.

Edit: The EntityKeyValue is a KeyValuePair<TKey, TValue> where the key is the primary key field of the entity and the value is the associated value.

E.g., I have an entity called Company whose primary key is the field Symbol.

var firstCompany = (from c in context.Companies select c).FirstOrDefault();
var kvp = firstCompany.EntityKey.EntityKeyValues[0];
// kvp shows {[Symbol, FOO]}

In my sandbox, I noticed this property was null when I created the entity in code. But once I read the entity from the database, it was correctly populated. So, it appears that the EF4 concept of a primary key only comes in to play once it hits the database. Although, you are free to set it explicitly ahead of time, if you wish.

SethO
A: 

So I was finally able to find out how to get this to work. I wish I hadn't lost the link to the blog I read last night as I didn't write the code.

       public T GetByPrimaryKey<T>(int id) where T : class
    {
        return (T)_db.GetObjectByKey(new EntityKey(_db.DefaultContainerName + "." + this.GetEntityName<T>(), GetPrimaryKeyInfo<T>().Name, id));
    }
        string GetEntityName<T>()
    {
            string name = typeof(T).Name;
            if (name.ToLower() == "person")
                return "People";
            else if (name.Substring(name.Length - 1, 1).ToLower() == "y")
                return name.Remove(name.Length - 1, 1) + "ies";
            else if (name.Substring(name.Length - 1, 1).ToLower() == "s")
                return name + "es";
            else
                return name + "s";
    }

    private PropertyInfo GetPrimaryKeyInfo<T>()
    {
        PropertyInfo[] properties = typeof(T).GetProperties();
        foreach (PropertyInfo pI in properties)
        {
            System.Object[] attributes = pI.GetCustomAttributes(true);
            foreach (object attribute in attributes)
            {
                if (attribute is EdmScalarPropertyAttribute)
                {
                    if ((attribute as EdmScalarPropertyAttribute).EntityKeyProperty == true)
                        return pI;
                }
                else if (attribute is ColumnAttribute)
                {

                    if ((attribute as ColumnAttribute).IsPrimaryKey == true)
                        return pI;
                }
            }
        }
        return null;
    }

I hope this helps someone else. All I can say is that it should be a little clearer on how to do this.

runxc1 Bret Ferrier
I'm guessing it was from: http://www.guerrillasyntax.com/index.php/2010/04/24/linq-to-entity-generic-repository/
Brad Leach
That was it. Good find
runxc1 Bret Ferrier