Generic Repository for Entity Framework for Pluralized Entity Set

by Mikael Henriksson 5. April 2010 00:13

UPDATE: I must apologize for the previously incomplete version of this class. I failed to see that in the case where we don’t have an instantiated entity there is a null EntityKey of course. This is an easy fix with extensions on the object context.

using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
public static class ObjectContextExtensions
{
	internal static EntitySetBase GetEntitySet<TEntity>(this ObjectContext context)
	{
		EntityContainer container = 
				context.MetadataWorkspace
					.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
		Type baseType = GetBaseType(typeof(TEntity));
		EntitySetBase entitySet = container.BaseEntitySets
			.Where(item => item.ElementType.Name.Equals(baseType.Name))
			.FirstOrDefault();

		return entitySet;
	}

	private static Type GetBaseType(Type type)
	{
		var baseType = type.BaseType;
		if (baseType != null && baseType != typeof(EntityObject))
		{
			return GetBaseType(type.BaseType);
		}
		return type;
	}
}

A while back I wrote a post about a generic repository for Entity Framework. That post was intended for version 1 of the framework and it had a big big flaw. It doesn’t take in consideration that the EntitySetName might not be the same as the name of the Entity. Let me try to explain, with the soon to be release of the Entity Framework they have added pluralization to the names of the entity sets. This means that our entity set of “Product” most likely becomes “Products”. At least I just ran in to this issue :)

The fix though is not far away. All we need to do is add a restriction to the generic parameter and we get direct access to the EntitySetName. All our entities (at least with letting EF handle the generation of the entities from database) inherits from IEntityWithKey that is just the property EntityKey which in turn has a property named EntitySetName that we can use.

The updated code will look like below. I do Activator.CreateInstance<T> because I need an instantiated object to be able to get the EntityKey (it was the first thing that sprung to mind). This is of course only done when no entity is passed in as a parameter. Also the generic constraint in the top of the class.

public class Repository<T> 
	where T : IEntityWithKey
{
	public int Add(T entity)
	{
		var context = GetObjectContext();
		context.AddObject(context.GetEntitySet<T>().Name, entity);
		int saveValue = context.SaveChanges();
		ReleaseObjectContextIfNotReused();

		return saveValue;
	}

	public long Count()
	{
		var context = GetObjectContext();
		int count = context
			.CreateQuery<T>("[" + context.GetEntitySet<T>().Name + "]")
			.Count();
			
		ReleaseObjectContextIfNotReused();

		return count;
	}

	public long Count(Expression<Func<T, bool>> criterion)
	{
		var context = GetObjectContext();
		int count = context
			.CreateQuery<T>("[" + context.GetEntitySet<T>().Name + "]")
			.Count(criterion);
		ReleaseObjectContextIfNotReused();
		return count;
	}

	public int Delete(T entity)
	{
		var context = GetObjectContext();

		object originalItem;
		EntityKey key = context.CreateEntityKey(entity.EntityKey.EntitySetName, entity);
		if (context.TryGetObjectByKey(key, out originalItem))
		{
			context.DeleteObject(originalItem);
		}
		int returnValue = context.SaveChanges();
		ReleaseObjectContextIfNotReused();

		return returnValue;
	}

	public IList<T> GetAll()
	{
		var context = GetObjectContext();
		var entities = context
			.CreateQuery<T>("[" + context.GetEntitySet<T>().Name + "]")
			.ToList();
		ReleaseObjectContextIfNotReused();

		return entities;
	}

	public IList<T> GetAll(Expression<Func<T, bool>> criterion)
	{
		var context = GetObjectContext();
		var entities = context
			.CreateQuery<T>("[" + context.GetEntitySet<T>().Name + "]")
			.Where(criterion)
			.ToList();
			
		ReleaseObjectContextIfNotReused();

		return entities;
	}

	public T GetSingle(Expression<Func<T, bool>> criterion)
	{
		var context = GetObjectContext();
		T entity = context
			.CreateQuery<T>("[" + context.GetEntitySet<T>().Name + "]")
			.First(criterion);
		ReleaseObjectContextIfNotReused();

		return entity;
	}

	public int Update(T entity)
	{
		var context = GetObjectContext();

		object originalItem;
		Type type = typeof (T);


		EntityKey key = context.CreateEntityKey(entity.EntityKey.EntitySetName, entity);

		if (context.TryGetObjectByKey(key, out originalItem))
		{
			context.ApplyPropertyChanges(key.EntitySetName, entity);
		}
		int returnValue = context.SaveChanges();
		ReleaseObjectContextIfNotReused();

		return returnValue;
	}
}

Tags:

Entity Framework

blog comments powered by Disqus

About the author

Life architect specialized in programming