bamblack
bamblack

Reputation: 3779

Repository method to find an entity with a non-Primary Key value

So I'm not sure if I got the terminology in the title correct, but I should be able to describe what I'm trying to do.

I have a "GenericRepository" class that all of my actual Repository classes inherit from and it contains several generic methods. The code for that class is below

public class GenericRepository<TEntity> where TEntity : class
{
    internal ReportsDirectoryEntities context;
    internal DbSet<TEntity> dbSet;

    public GenericRepository(ReportsDirectoryEntities context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    /////////////////////////////////////////////////////////////////////

    public virtual IEnumerable<TEntity> GetAll()
    {
        return dbSet.ToList();
    }
    public virtual TEntity GetByID(int id)
    {
        return dbSet.Find(id);
    }
    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }
    public virtual void Delete(object id)
    {
        TEntity entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }
    public virtual void Delete(TEntity entityToDelete)
    {
        if (context.Entry(entityToDelete).State == System.Data.EntityState.Detached)
        {
            dbSet.Attach(entityToDelete);
        }
        dbSet.Remove(entityToDelete);
    }
    public virtual void Update(TEntity entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = System.Data.EntityState.Modified;
    }
}

Now all of that works great, what I'm trying to do now is add a GetByName method where I can pass in the "Name" property of an entity and it returns it.

I've tried

    public virtual TEntity GetByName(string name)
    {
        return (from e in context.Set<TEntity>()
                where e.Name == name
                select e).SingleOrDefault();
    } 

but that underlines Name saying that "TEntity does not contain a defintion for name" so on and so forth, which makes sense.

I can easily do this in the individual repositories but was wondering if there was a way I could do it in the Generic one.

Any help is greatly appreciated!

Upvotes: 1

Views: 3134

Answers (3)

Derrick Moeller
Derrick Moeller

Reputation: 4970

If all of your entities use Name as a property, you just need to create an interface and use it as a constraint for your generic class.

Interface:

public interface IStringName
{
    public string Name { get; set; }
}

Entity:

public partial class MyEntity : IStringName
{
    public String Name { get; set; }
    // Additional properties here.
}

Repository:

public class GenericRepository<TEntity> where TEntity : class, IStringName
{
    // Your code here.
}

If one of your entities doesn't use the Name property, you have a couple of options. You can change an appropriate property name within the entity to Name, and map it appropriately to the original storagemodel name. Alternatively you could inherit the original generic repository, something like.

NamedRepository:

public class NamedRepository<TEntity> : GenericRepository<TEntity>
    where TEntity : class, IStringName
{
    public NamedRepository(ReportsDirectoryEntities context) : base(context)
    {
    }

    public virtual TEntity GetByName(string name)
    {
        return (from e in base.context.Set<TEntity>()
                where e.Name == name
                select e).SingleOrDefault();
    }
}

This approach would allow you to use the NamedRepository on entities that do implement your new interface, and your original GenericRepository for those that do not.

Upvotes: 1

Dima
Dima

Reputation: 6741

If all your entities types have Name property, then you can extend TEntity inteface with Name property.

Otherwise, you can use SqlQuery() method of DbSet with string query as a parameter.

Upvotes: 2

D Stanley
D Stanley

Reputation: 152634

In order to do this cleanly TEntity needs to be constrained to a type (or interface) that has a Name property. Otherwise you're using reflection (or dynamic to access the Name property) which allows errors that wouldn't be caught until run-time.

Upvotes: 1

Related Questions