iLemming
iLemming

Reputation: 36166

Help me with architectural question

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

Answers (4)

Mike Dinescu
Mike Dinescu

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

StriplingWarrior
StriplingWarrior

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

Bertrand Marron
Bertrand Marron

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

Kyle W
Kyle W

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

Related Questions