Reputation: 36166
Here is the simple snippet:
public interface IRepository<T>
{
T Get(int id);
}
public class Repository<T> : IRepository<T> where T : class
{
private readonly IRepositoryContext _repositoryContext;
private IObjectSet<T> _objectSet;
public Repository(IRepositoryContext repositoryContext)
{
_repositoryContext = repositoryContext;
_objectSet = repositoryContext.GetObjectSet<T>();
}
public virtual T Get(int id)
{
return ObjectSet.First(x=> x.Id == id)
// that wouldn't work because there is no guarantee that T entity has Id property
}
Now, as you can see I can instantiate Repository for any entity object and use methods defined (although in the example we have only Get(). But I can't use any constrains in expressions, unless I create non-abstract classes for each entity of T
based on IRepository<T>
and then implement methods the way I want.
But what if I need to use a method like Get
which implementation stays the same for probably all entities (every entity has an Id).
How to do that?
I thought, I could create an abstract entity in EF data model designer, mapped to nothing, and mark that as the base type for all other entities, but It's asking for a mapping i.e. table. I tried to go with a complex type instead - that wouldn't let me inherit from it.
So, show me please the most efficient way to achieve that
Upvotes: 1
Views: 88
Reputation: 55720
If every entity has the Id property, you could create an interface which exposes the Id property make each entity implement the interface.
//Let's call the interface IIDInterface
public interface IIDInterface
{
int Id { get; }
}
// Now, suppose one of your entity classes is called: EntityOne
public class EntityOne : IIDInterface
{
// .. class constructors etc.
// the IIDInterface interface method implementation
public int Id
{
get
{
// getter implementation goes here
}
}
// .. other members of the class
}
Then you could cast to the interface in the implementation of Get and if the cast succeeds use the .Id property. The Get function becomes:
public virtual T Get(int id)
{
if(T is IIDInterface)
return ObjectSet.First(x => ((IIDInterface)x).Id == id)
return default(T);
}
EDIT
Another solution could be to use reflection and use the Id property of the T object if it has one. But doing that might be slower and I think the overhead is unnecessary unless you can't modify the entity objects to implement the IIDInterface interface (i.e. if you don't own them)
Upvotes: 0
Reputation: 156524
The best way to do what you're asking would probably be to build a custom expression. Something like this:
public Expression<Func<T, bool>> GetIdEqualsExpression(int id)
{
var idProp = typeof(T).GetProperty("ID");
var param = Expression.Parameter(typeof(T), "t");
return Expression.Lambda<Func<T, bool>>(
Expression.Equal(Expression.PropertyOrField(param, "ID"),
Expression.Constant(id)), param);
}
public virtual T Get(int id)
{
return ObjectSet.AsQueryable().Single(GetIdEqualsExpression(id));
}
That said, Bertrand makes a good point in stating that maybe it should be the responsibility of the calling code to provide the expression, so you don't risk runtime exceptions.
Upvotes: 1
Reputation: 22210
That's not really the repository's role to know whether there's an ID or not in your object.
Most repositories implement the Single
method like this
public T Single( Expression<Func<T, bool>> predicate ) {
return ObjectSet.Single<T>( predicate );
}
Upvotes: 4
Reputation: 3752
You can use an interface:
public interface IEntity { int Id { get; } }
then instead of "where T : class" you can do "where T : IEntity" and any object will have a retrievable Id.
Upvotes: 0