Entity Framework GetById<T>
I was asked the other day if I knew of a way to do GetById generically with Entity Framework. I thought that I had done something similiar in the past and the task would be easy, but it turned out to be a little bit more complicated.
After searching some forums and blogs, I came up with a result that I like a bit. First we will start with a simple test:
public class When_Getting_Product_By_Id : Rollback_Specification_Context<Container>
{
[Test]
public void Should_Return_Product()
{
Assert.IsNotNull(_product);
}
[Test]
public void Should_Have_Proper_Id()
{
Assert.AreEqual(_productId, _product.ProductId);
}
private long _productId;
private Product _product;
protected override void Because()
{
_product = Repository.GetById<Product, long>(_productId);
}
protected override void Context()
{
base.Context();
_productId = 1498;
}
}
I will create an IRepository, which will be the base interface for all of my entity repositories:
public interface IRepository
{
T GetById<T, TId>(TId id);
}
Then an IProductRepository, which is empty for the moment, but would have other items in it in the future:
public interface IProductRepository : IRepository
{
}
I wanted to use Expressions to put together a dynamic lamda expression, so I have a method to get the property info. Some of this idea was stolen and changed from a forum post that I read. What it is ultimately doing is looking for the Primary Key column for the particular entity that is passed in and returning the info for that property:
public PropertyInfo GetKeyPropertyInfo(Type type)
{
var properties = type.GetProperties();
foreach (var propertyInfo in properties)
{
var attributes = propertyInfo.GetCustomAttributes(true);
if (attributes.Any(attribute => attribute is EdmScalarPropertyAttribute && ((EdmScalarPropertyAttribute)attribute).EntityKeyProperty))
{
return propertyInfo;
}
}
throw new ApplicationException(String.Format("No key property found for type {0}", type.Name));
}
Then I create the method for getting the id generically. At first, I just had GetById<T>(long id), but then I didn’t want someone to be held back by the type of id, in case they wanted to use a Guid or a long or anything else they desired:
public T GetById<T, TId>(TId id)
{
var type = typeof (T);
// x
ParameterExpression argument = Expression.Parameter(type, "x");
Expression expression = argument;
// x.Id == id
expression = Expression.Property(expression, GetKeyPropertyInfo(type));
var valueExpression = Expression.Constant(id);
expression = Expression.Equal(expression, valueExpression);
// x => x.Id == id
var lamda = Expression.Lambda<Func<T, bool>>(expression, argument);
var container = this.MetadataWorkspace.GetEntityContainer(this.DefaultContainerName, DataSpace.CSpace);
var set = (from meta in container.BaseEntitySets
where meta.ElementType.FullName == type.ToString()
select meta).First();
return this.CreateQuery<T>("[" + set.Name + "]").FirstOrDefault(lamda);
}
I have tried to document what I am doing for those that are unfamiliar with Expressions. I am ultimately building up the lamda expression of “x => x.Id == id”, where the id is the value passed in and the x.Id is the property that has the key on it.
I then go through the BaseEntitySets on the container looking for the set that matches my entity. Careful on this part, your namespace for your container needs to match the project name that your models are in.
Once I find the set, I can then create the query and apply the lamda expression to find the product that I am looking for.
So the final code looks like this:
public partial class Container
{
public T GetById<T, TId>(TId id)
{
var type = typeof (T);
// x
ParameterExpression argument = Expression.Parameter(type, "x");
Expression expression = argument;
// x.Id == id
expression = Expression.Property(expression, GetKeyPropertyInfo(type));
var valueExpression = Expression.Constant(id);
expression = Expression.Equal(expression, valueExpression);
// x => x.Id == id
var lamda = Expression.Lambda<Func<T, bool>>(expression, argument);
var container = this.MetadataWorkspace.GetEntityContainer(this.DefaultContainerName, DataSpace.CSpace);
var set = (from meta in container.BaseEntitySets
where meta.ElementType.FullName == type.ToString()
select meta).First();
return this.CreateQuery<T>("[" + set.Name + "]").FirstOrDefault(lamda);
}
public PropertyInfo GetKeyPropertyInfo(Type type)
{
var properties = type.GetProperties();
foreach (var propertyInfo in properties)
{
var attributes = propertyInfo.GetCustomAttributes(true);
if (attributes.Any(attribute => attribute is EdmScalarPropertyAttribute && ((EdmScalarPropertyAttribute)attribute).EntityKeyProperty))
{
return propertyInfo;
}
}
throw new ApplicationException(String.Format("No key property found for type {0}", type.Name));
}
}
And now my ProductRepository is a Partial of the Container and the ProductRepository implements the IProductRepository, so it has the GetById on it. The tests pass with flying colors of GREEN, so I am done.


