Reputation: 3779
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
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
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
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